JVM----內存區域及溢出異常

JVM內存區域

  

程序計數器:

一個較小的內存空間,可以看做是當前執行代碼的行號指示器,分支、循環、跳轉、異常處理、線程方面都要依賴這個計數器完成。

這個程序計數器是線程隔離的,即每個線程有唯一的程序計數器。Native方法是不會在程序計數器內計數的,只會爲Null。

這個區域的異常不會導致OutOfMemoryError。

 

JVM棧:

主要用於描述java方法執行的內存模型,程序計數器只是單純的記錄行號,而JVM棧還保存了方法中的局部變量表、操作棧、動態鏈接、方法出入口等信息,對於很多人來說JVM棧的主要作用是存儲了方法內的局部變量表(變量表直接存儲基本數據類型和引用數據類型的引用),局部變量表所需內存在編譯器就已經確定,不會改變。

這塊內存區域和程序計數器一樣都是線程隔離的,即各個線程只有一個。

這塊內存區域當JVM棧請求的棧深度超出系統分配時,會報StackOverFlowError。

當這塊內存區域在動態擴展時(一般都可以動態擴展),如果物理內存無法滿足擴展請求,會報OutOfMemoryError。

 

本地方法棧:

和JVM棧相似,唯一的區別就是JVM棧是專門爲JVM字節碼方法準備的,而本地方法棧是爲Native方法服務的

 

堆:

這是JVM中內存佔用最大的地方,一般情況下對象實例以及數據都是在堆這裏分配內存進行實例化。這裏也是GC主要工作的地方。這裏主要分爲新生代、老生代及永生代區,後邊說到的GC會對這裏的分代進行說明。

這塊內存區域是所有線程共享的。

當程序中的對象實例創建過多致使內存不夠分配的時候,會造成OutOfMemoryError。

 

方法區:

這個區域主要存儲已被虛擬機加載的類信息、常量、靜態變量以及動態代理生成的類信息等。

和堆一樣這塊內存區域也是線程共享的。

同樣,當無法完成內存分配時,拋出OutOfMemoryError。

 

棧中的reference類型定位到堆中對象實例的兩種方式:

使用句柄方式:


使用直接指針方式(HotSpot使用):

 

溢出異常:

堆溢出

堆溢出就是給堆分配內存(不斷創建引用類型對象),由於內存限制無法繼續分配的情況,報錯信息是java.lang.OutOfMemoryError: Java heapspace,在啓動快照的情況下還有Dumping heap to java_pid2712.hprof ...這個信息指明快照文件。通過啓用參數-XX+HeapDumpOnOutOfMemoryError,在Tomcat下可以通過修改catalina.shcatalina.bat文件修改JVM參數添加-XX:+HeapDumpOnOutOfMemoryError參數保存溢出快照

 

棧溢出

棧溢出一般情況下是指JVM棧、本地方法棧溢出,這種情況下的溢出一般分爲兩種情況:

單線程運行,一般報錯信息爲java.lang.StackOverflowError,這種情況有堆棧信息,比較容易排查

多線程運行,一般報錯信息爲java.lang.OutOfMemoryError

在我們實際開發中,單線程情況很少,因爲我們使用的Web容器(Tomcat等)都是多線程的,所以較爲常見的還是第二種情況。在第二種情況下,我們可以通過設置減少堆內存或減少棧容量來調優。主要原因是,棧的容量由 JVM內存-堆內存-方法區內存-程序計數器內存 來決定。

 

溢出快照分析

可以通過Eclipse的MAT(Memory Analyser Tools)來分析溢出產生的快照文件。該工具可以生成一個HTML格式的分析報告,通過異常發生時的內存佔用餅狀圖、變量(對象)引用樹的方式較爲準確的報告出產生內存溢出的代碼位置或相關信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章