Java虛擬機系列(三)---內存溢出情況及解決方法
因為Java虛擬機內存有堆內存、方法區、虛擬機棧、本地方法棧和程序計數器五部分組成,其中程序計數器是唯一一塊不會發生內存溢出異常的內存區,所以只有四類內存區可能發生內存溢出異常,其中虛擬機棧和本地方法棧都是Java方法執行的內存模型,所以它們的異常發生情況幾乎相同,另外,在方法區中。又有一塊內存是常量池,所以內存溢出的情況可分為Java堆溢出、虛擬機棧和本地方法棧溢出、方法區和運行時常量池溢三種情況。
一、Java堆溢出
1、產生的原因:因為堆中存放的是對象實例和數組,所以當對象數量>最大堆容量限制時,就會發生內存溢出異常;
2、解決方案:
1)如果對象不是必須的,但是又有指向GC Root(後面章節介紹)的引用鏈,此時無法被GC,就會出現內存泄露,可通過定位泄露原因在代碼中找到解決方案;
2)如果對象是必須的,就要檢查虛擬機棧的堆參數能否調大(當虛擬機內存總容量小於物理內存時可以調大),如果能調大可通過修改該參數來解決:
--Xmx:最大堆內存
--Xms:最小堆內存
如果--Xmx和--Xms相同,則說明堆內存不可動態擴展。
二、虛擬機棧和本地方法棧溢出
1、發生內存溢出的原因:
由於在HotSpot虛擬機中,不區分虛擬機棧和本地方法棧,所以設置本地方法棧大小的參數--Xoss無效,一般通過--Xss參數設置棧容量(我猜測ss是stack size的縮寫,這樣比較好記)
虛擬機中定義了兩種異常情況:
1)當線程申請的棧深度超過虛擬機允許的最大棧深度時,會發生StackOverflowError異常;
2)當棧內存擴展時,如果不能申請到足夠的內存,就會發生OutOfMemoryError異常
我們知道這部分內存是線程私有的,每個線程都需要分配一塊內存,所以當線程很多時就會發生內存溢出,下面來分析一下這句話背後的原理:
①內存容量=堆內存+方法區內存+程序計數器內存(可忽略)+棧內存(虛擬機棧和本地方法棧);
②因為棧容量在編譯器就可知,且一旦分配在運行期就不會改變,在棧容量一定的情況下,每個虛擬機棧分配到的棧容量越大,可以創建的線程數就越少;
③當線程過多時,就會導致棧容量不足,從而發生內存溢出;
2、解決方法:
首先,判斷能不能減少線程數,如果能則減少線程數;如果不能減少線程數,就只能
1)--Xmx:減少
2)--Xss:減少
三、方法區和運行時常量池溢出
1、異常發生原因
方法區主要存儲class的相關信息,如類,名、訪問修飾符、常量池、字段描述信息、方法描述信息等,所以如果運行時產生大量的類去填滿方法區,就能出現內存溢出異常。這裏就涉及到如何動態產生大量類的方法,一般有如下兩種:
1)使用反射機制或動態代理
2)使用CGLib直接操作字節碼
2、解決方法:
通過調節方法區大小參數--XX:PermSize和-XX:MaxPermSize限制方法區大小,當設置成相同的值時不可擴展。
除以上三種虛擬機內存溢出情況之外,還有一種本機直接內存溢出,可通過調節參數-XX:MaxDirectMemorysize指定,若不指定,則和Java堆內存大小一樣。
以上就是Java虛擬機中的幾種內存溢出情況及解決方法。
Java虛擬機系列(三)---內存溢出情況及解決方法