轉載於:https://blog.csdn.net/shimiso/article/details/21830871html
雖然jvm能夠經過GC自動回收無用的內存,可是代碼很差的話仍然存在內存溢出的風險。java
最近在網上搜集了一些資料,現整理以下:數據庫
1、爲何要了解內存泄露和內存溢出?數組
一、內存泄露通常是代碼設計存在缺陷致使的,經過了解內存泄露的場景,能夠避免沒必要要的內存溢出和提升本身的代碼編寫水平;緩存
二、經過了解內存溢出的幾種常見狀況,能夠在出現內存溢出的時候快速的定位問題的位置,縮短解決故障的時間。jvm
2、基本概念ui
理解這兩個概念很是重要。spa
內存泄露:指程序中動態分配內存給一些臨時對象,可是對象不會被GC所回收,它始終佔用內存。即被分配的對象可達但已無用。.net
內存溢出:指程序運行過程當中沒法申請到足夠的內存而致使的一種錯誤。內存溢出一般發生於OLD段或Perm段垃圾回收後,仍然無內存空間容納新的Java對象的狀況。線程
從定義上能夠看出內存泄露是內存溢出的一種誘因,不是惟一因素。
3、內存泄露的幾種場景:
一、長生命週期的對象持有短生命週期對象的引用
這是內存泄露最多見的場景,也是代碼設計中常常出現的問題。
例如:在全局靜態map中緩存局部變量,且沒有清空操做,隨着時間的推移,這個map會愈來愈大,形成內存泄露。
二、修改hashset中對象的參數值,且參數是計算哈希值的字段
當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段,不然對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法使用該對象的當前引用做爲參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中刪除當前對象,形成內存泄露。
三、機器的鏈接數和關閉時間設置
長時間開啓很是耗費資源的鏈接,也會形成內存泄露。
4、內存溢出的幾種狀況:
一、堆內存溢出(outOfMemoryError:java heap space)
在jvm規範中,堆中的內存是用來生成對象實例和數組的。
若是細分,堆內存還能夠分爲年輕代和年老代,年輕代包括一個eden區和兩個survivor區。
當生成新對象時,內存的申請過程以下:
a、jvm先嚐試在eden區分配新建對象所需的內存;
b、若是內存大小足夠,申請結束,不然下一步;
c、jvm啓動youngGC,試圖將eden區中不活躍的對象釋放掉,釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;
d、Survivor區被用來做爲Eden及old的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,不然會被保留在Survivor區;
e、 當OLD區空間不夠時,JVM會在OLD區進行full GC;
f、full GC後,若Survivor及OLD區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立內存區域,則出現」out of memory錯誤」:
outOfMemoryError:java heap space
代碼舉例:
/** * 堆內存溢出 * * jvm參數:-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m * */ public class MemoryLeak { private String[] s = new String[1000]; public static void main(String[] args) throws InterruptedException { Map<String,Object> m =new HashMap<String,Object>(); int i =0; int j=10000; while(true){ for(;i<j;i++){ MemoryLeak memoryLeak = new MemoryLeak(); m.put(String.valueOf(i), memoryLeak); } } } }
二、方法區內存溢出(outOfMemoryError:permgem space)
在jvm規範中,方法區主要存放的是類信息、常量、靜態變量等。
因此若是程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能致使該區發生內存溢出,通常該區發生內存溢出時的錯誤信息爲:
outOfMemoryError:permgem space
代碼舉例:
三、線程棧溢出(java.lang.StackOverflowError)
線程棧時線程獨有的一塊內存結構,因此線程棧發生問題一定是某個線程運行時產生的錯誤。
通常線程棧溢出是因爲遞歸太深或方法調用層級過多致使的。
發生棧溢出的錯誤信息爲:
java.lang.StackOverflowError
代碼舉例:
/** * 線程操做棧溢出 * * 參數:-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m -Xss64k * */ public class StackOverflowTest { public static void main(String[] args) { int i =0; digui(i); } private static void digui(int i){ System.out.println(i++); String[] s = new String[50]; digui(i); } }
5、爲了不內存泄露,在編寫代碼的過程當中能夠參考下面的建議:
一、儘早釋放無用對象的引用
二、使用字符串處理,避免使用String,應大量使用StringBuffer,每個String對象都得獨立佔用內存一塊區域
三、儘可能少用靜態變量,由於靜態變量存放在永久代(方法區),永久代基本不參與垃圾回收
四、避免在循環中建立對象
五、開啓大型文件或從數據庫一次拿了太多的數據很容易形成內存溢出,因此在這些地方要大概計算一下數據量的最大值是多少,而且設定所需最小及最大的內存空間值。
參考:
http://zhidao.baidu.com/question/263477119.html
https://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/ Java的內存泄漏
http://jelly-x.iteye.com/blog/1120406 JVM內存分析及致使內存溢出
http://wenku.baidu.com/view/ef3158fc04a1b0717fd5ddda.html java內存泄露和內存溢出