您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關Java內存溢出和內存泄露的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
一、為什么要了解內存泄露和內存溢出?
1、內存泄露一般是代碼設計存在缺陷導致的,通過了解內存泄露的場景,可以避免不必要的內存溢出和提高自己的代碼編寫水平;
2、通過了解內存溢出的幾種常見情況,可以在出現內存溢出的時候快速的定位問題的位置,縮短解決故障的時間。
二、基本概念
理解這兩個概念非常重要。
內存泄露:指程序中動態分配內存給一些臨時對象,但是對象不會被GC所回收,它始終占用內存。即被分配的對象可達但已無用。
內存溢出:指程序運行過程中無法申請到足夠的內存而導致的一種錯誤。內存溢出通常發生于OLD段或Perm段垃圾回收后,仍然無內存空間容納新的Java對象的情況。
從定義上可以看出內存泄露是內存溢出的一種誘因,不是唯一因素。
三、內存泄露的幾種場景:
1、長生命周期的對象持有短生命周期對象的引用
這是內存泄露最常見的場景,也是代碼設計中經常出現的問題。
例如:在全局靜態map中緩存局部變量,且沒有清空操作,隨著時間的推移,這個map會越來越大,造成內存泄露。
2、修改hashset中對象的參數值,且參數是計算哈希值的字段
當一個對象被存儲進HashSet集合中以后,就不能修改這個對象中的那些參與計算哈希值的字段,否則對象修改后的哈希值與最初存儲進HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作為參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中刪除當前對象,造成內存泄露。
3、機器的連接數和關閉時間設置
長時間開啟非常耗費資源的連接,也會造成內存泄露。
四、內存溢出的幾種情況:
1、堆內存溢出(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); } } } }
2、方法區內存溢出(outOfMemoryError:permgem space)
在jvm規范中,方法區主要存放的是類信息、常量、靜態變量等。
所以如果程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能導致該區發生內存溢出,一般該區發生內存溢出時的錯誤信息為:
outOfMemoryError:permgem space
代碼舉例:
1.jvm參數:-XX:PermSize=2m -XX:MaxPermSize=2m
2.將方法區的大小設置很低即可,在啟動加載類庫時就會出現內存不足的情況
3、線程棧溢出(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); } }
五、為了避免內存泄露,在編寫代碼的過程中可以參考下面的建議:
1、盡早釋放無用對象的引用
2、使用字符串處理,避免使用String,應大量使用StringBuffer,每一個String對象都得獨立占用內存一塊區域
3、盡量少用靜態變量,因為靜態變量存放在永久代(方法區),永久代基本不參與垃圾回收
4、避免在循環中創建對象
5、開啟大型文件或從數據庫一次拿了太多的數據很容易造成內存溢出,所以在這些地方要大概計算一下數據量的最大值是多少,并且設定所需最小及最大的內存空間值。
感謝各位的閱讀!關于“Java內存溢出和內存泄露的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。