深入理解java虛擬機 摘要
目錄:
一、自動內存管理機制
一、自動內存管理機制
3. 實戰:OutOfMemoryError異常
-
Java堆溢出:
測試代碼:
public class Tests { static class Obj{ } public static void main(String[] args) throws >Exception { List<Obj> list = new ArrayList<>(); while (true){ list.add(new Obj()); } } }
啟動參數:
-Xmx20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
將堆的最小值-Xms參數與最大值-Xmx參數設置為一樣即可避免堆自動擴展,通過參數-XX:+HeapDumpOnOutOfMemoryError可以讓虛擬機在出現內存溢
出異常時Dump出當前的內存堆轉儲快照以便事后進行分析要解決這個區域的異常,一般的手段是先通過內存映像分析工具
ideal可使用JProfiler和JMeter插件進行分析 -
虛擬機棧和本地方法棧溢出
測試代碼:
public static class JavaVMStackSOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } } public static void main(String[]args)throws Throwable{ JavaVMStackSOF oom=new JavaVMStackSOF(); try{ oom.stackLeak(); }catch(Throwable e){ System.out.println("stack length:"+oom.stackLength); throw e; } }
啟動參數:
-Xss20m
對于HotSpot來說,雖然-Xoss參數(設置本地方法棧大小)存在,但實際上是無效的,棧容量只由-Xss參數設定
如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常。如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。
這里把異常分成兩種情況,看似更加嚴謹,但卻存在著一些互相重疊的地方:當棧空間無法繼續分配時,到底是內存太小,還是已使用的棧空間太大,其本質上只是對同一件事情的兩種描述而已。
在單個線程下,無論是由于棧幀太大還是虛擬機棧容量太小,當內存無法分配的時候,虛擬機拋出的都是StackOverflowError異常。
-
本機直接內存溢出
DirectMemory容量可通過-XX:MaxDirectMemorySize指定,如果不指定,則默認與Java堆最大值(-Xmx指定)一樣
測試代碼:
public class Tests { private static final Long MB=1024L*1024L; public static void main(String[]args)throws Exception{ Field unsafeField=Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe=(Unsafe)unsafeField.get(null); while(true){ unsafe.allocateMemory(MB); } } }
啟動參數:
-Xss20m
代碼解釋:
越過了DirectByteBuffer類,直接通過反射獲取Unsafe實例進行內存分配(Unsafe類的getUnsafe()方法限制了只有引導類加載器才會返回實例,也就是設計者希望只有rt.jar中的類才能使用Unsafe的功能)。因為,雖然使用DirectByteBuffer分配內存也會拋出內存溢出異常,但它拋出異常時并沒有真正向操作系統申請分配內存,而是通過計算得知內存無法分配,于是手動拋出異常,真正申請分配內存的方法是unsafe.allocateMemory()。由DirectMemory導致的內存溢出,一個明顯的特征是在Heap Dump文件中不會看見明顯的異常,如果讀者發現OOM之后Dump文件很小,而程序中又直接或間接使用了NIO,那就可以考慮檢查一下是不是這方面的原因。