內存溢出:簡單地說內存溢出就是指程序運行過程當中申請的內存大於系統可以提供的內存,致使沒法申請到足夠的內存,因而就發生了內存溢出。java
內存泄漏:內存泄漏指程序運行過程當中分配內存給臨時變量,用完以後卻沒有被GC回收,始終佔用着內存,既不能被使用也不能被分配給其餘程序,因而就發生了內存泄漏。算法
內存溢出有如下幾種常見的狀況:數據庫
(1)java.lang.OutOfMemoryError: PermGen space (持久帶溢出)網絡
咱們知道jvm經過持久帶實現了java虛擬機規範中的方法區,而運行時常量池就是保存在方法區中的,所以發生這種溢出多是運行時常量池溢出,或是因爲程序中使用了大量的jar或class,使得方法區中保存的class對象沒有被及時回收或者class信息佔用的內存超過了配置的大小。jvm
(2) java.lang.OutOfMemoryError: Java heap space (堆溢出)socket
發生這種溢出的緣由通常是建立的對象太多,在進行垃圾回收以前對象數量達到了最大堆的容量限制。工具
解決這個區域異常的方法通常是經過內存映像分析工具對Dump出來的堆轉儲快照進行分析,看究竟是內存溢出仍是內存泄漏。若是是內存泄漏,可進一步經過工具查看泄漏對象到GC Roots的引用鏈,定位出泄漏代碼的位置,修改程序或算法;若是不存在泄漏,就是說內存中的對象確實都還必須存活,那就應該檢查虛擬機的堆參數-Xmx(最大堆大小)和-Xms(初始堆大小),與機器物理內存對比看是否能夠調大。spa
(3)虛擬機棧和本地方法棧溢出線程
若是線程請求的棧深度大於虛擬機所容許的最大深度,將拋出StackOverflowErrorcode
若是虛擬機在擴展棧時沒法申請到足夠的內存空間,則拋出OutOfMemoryError
內存泄漏的根本緣由是長生命週期的對象持有短生命週期對象的引用,儘管短生命週期的對象已經再也不須要,但因爲長生命週期對象持有它的引用而致使不能被回收。
下面總結幾種常見的內存泄漏:
(1)靜態集合類引發的內存泄漏
HashTable的使用最容易出現內存泄漏,如HashMap,Vector等,這些變量的生命週期和程序一致,它們所持有的對象不能被釋放,從而形成內存泄漏。以下面的例子:
Vector<Object> v=new Vector<Object>(100); for (int i = 1; i<100; i++) { Object o = new Object(); v.add(o); o = null; }
循環申請Object對象,並將所申請的對象放入到一個Vector中,若是僅僅釋放引用自己(o=null),那麼Vector 仍然引用該對象,因此這個對象對GC 來講是不可回收的。所以,若是對象加入到Vector 後,還必須從Vector 中刪除,最簡單的方法就是將Vector對象設置爲null。
(2)修改HashSet中對象的參數值,且參數是計算哈希值的字段
當一個對象被存儲到HashSet集合中之後,修改了這個對象中那些參與計算哈希值的字段後,這個對象的哈希值與最初存儲在集合中的就不一樣了,這種狀況下,用contains方法在集合中檢索對象是找不到的,這將會致使沒法從HashSet中刪除當前對象,形成內存泄漏,舉例以下:
public static void main(String[] args) { Set<Person> set = new HashSet<Person>(); Person p1 = new Person("張三","1",25); Person p2 = new Person("李四","2",26); Person p3 = new Person("王五","3",27); set.add(p1); set.add(p2); set.add(p3); System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:3 個元素! p3.setAge(2); //修改p3的年齡,此時p3元素對應的hashcode值發生改變 set.remove(p3); //此時remove不掉,形成內存泄漏 set.add(p3); //從新添加,能夠添加成功 System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:4 個元素! for (Person person : set) { System.out.println(person); } }
注:實現這個例子時注意重寫Person類的hashCode方法。
(3)監聽器的使用
一般一個應用當中會用到不少監聽器,咱們會調用一個控件諸如addXXXListener()等方法來增長監聽器,但每每在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增長了內存泄漏的機會。
(4)各類鏈接
好比數據庫鏈接(dataSourse.getConnection()),網絡鏈接(socket)和io鏈接,除非其顯式的調用了其close()方法將其鏈接關閉,不然是不會自動被GC 回收的。對於Resultset 和Statement 對象能夠不進行顯式回收,但Connection 必定要顯式回收,由於Connection 在任什麼時候候都沒法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會當即爲NULL。可是若是使用鏈接池,狀況就不同了,除了要顯式地關閉鏈接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另一個也會關閉),不然就會形成大量的Statement 對象沒法釋放,從而引發內存泄漏。這種狀況下通常都會在try裏面去鏈接,在finally裏面釋放鏈接。
(1)儘早釋放無用對象的引用
(2)避免在循環中建立對象
(3)使用字符串處理時避免使用String,應使用StringBuffer
(4)儘可能少使用靜態變量,由於靜態變量存放在永久代,基本不參與垃圾回收
以上有錯誤或者須要補充的地方還請指點,謝謝