jvm運行時數據區域
java虛擬機在執行java程序的過程中將它所管理的內存划分為以下幾個運行時數據區域:
- 程序計數器(Program Counter Register)
- 虛擬機棧(VM Stack)
- 本地方法棧(Native Method Stack)
- 堆(Heap)
- 方法區(Method Area)
線程私有區域(程序計數器、虛擬機棧、本地方法棧),線程共享區域(堆、方法區),直接內存。線程私有數據區域生命周期與線程相同, 依賴用戶線程的啟動/結束 而 創建/銷毀(在 Hotspot VM 內)。線程共享區域隨虛擬機的啟動/關閉而創建/銷毀。直接內存並不是 JVM 運行時數據區的一部分。
1. 程序計數器
程序計數器是當前線程執行的字節碼的行號指示器,為了線程切換后能夠恢復到正確的執行位置,每條線程都需要一個獨立的程序計數器,個線程之間的程序計數器互不影響,各自獨立存儲。這類內存區域為“線程私有”的區域。此內存區域是唯一一個沒有OutOfMemoryError的區域。
2. 虛擬機棧
虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。棧容量只由-Xss參數設定
- 如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverFlowError
- 如果虛擬機在擴展時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常
/** * VM Args: -Xss160k * -Xss的最小值為160k **/ public 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; } } }
程序輸出:
stack length : 772 Exception in thread "main" java.lang.StackOverflowError at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11) at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
3. 本地方法棧
本地方法棧與虛擬機棧的作用相似,區別在於虛擬機方法棧為虛擬機執行Java方法服務,本地方法棧為虛擬機使用Native方法服務。Hotspot虛擬機將本地方法棧和虛擬機方法棧合二為一。
4. 堆
Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。所有創建的對象和數組都存放在堆上。Java堆是垃圾收集器管理的主要區域,垃圾收集器主要基於**分代收集算法**,因此從GC的角度對堆進行划分:新生代(Eden空間、From Survivor空間、To Survivor空間)、老年代。如果在堆中沒有內存完成示例分配、並且堆也無法再擴展,將會拋出OutOfMemoryError異常。堆的大小可以通過-Xmx(jvm運行時堆的最大值)和-Xms(jvm運行時堆的最小值)控制。
以下代碼測試堆的OutOfMemoryError異常,將堆的最小值-Xms參數與最大值-Xmx參數設置為一樣即可避免堆自動擴展,參數-XX:+HeapDumpOnOutOfMemoryError可讓虛擬機在出現內存溢出是Dump出當前的內存堆轉儲快照以便時候進行分析。
/** * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError **/ public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); } } }
輸出如下:
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid3421.hprof ... Heap dump file created [27644178 bytes in 0.129 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at HeapOOM.main(HeapOOM.java:18)
- -Xms : 設置堆的最小值,例如-Xms20, 分配20M
- -Xmx :設置堆的最大值,例如-Xmx200, 分配200M
- -XX:+HeapDumpOnOutOfMemoryError :讓虛擬機在出現內存溢出是Dump出當前的內存堆轉儲快照以便時候進行分析
5. 方法區
方法區用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
運行時常量池(Runtime Constant Pool)是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池。java虛擬機對class文件每一部分的格式都有嚴格的規定,每一個字節用於存儲哪種數據都必須符號規范上的要求才會被虛擬機認可、裝載和執行。
既然運行時常量池是方法區的一部分,自然受到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError的異常,測試方法區和運行時常量池溢出:
/** * jdk 1.8及之后,VM Args: -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m * jdk 1.8及之前,VM Args: -XX:PermSize=2m -XX:MaxPermSize=2m **/ public class RuntimeConstantPoolOOM { public static void main(String[] args) { List<String> list = new ArrayList<>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } }
程序輸入:
Error occurred during initialization of VM
java.lang.OutOfMemoryError: Metaspace
<<no stack trace available>>
6. 直接內存(Direct Memory)
直接內存(Direct Memory)並不是虛擬機運行時的數據區的一部分,也不是java虛擬機規范中定義的內存區域,不會受到java堆大小的限制,不受jvm gc的管理。這部分內存如果頻繁使用,也可能導致OutOfMemoryError異常, 測試本機直接內存溢出,DirectMemory容量可通過-XX:MaxDirectMemorySize設置,如果不指定,默認與Java堆最大值(-Xmx指定)一樣
/** * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M **/ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; 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(_1MB); } } }
通過測試,始終沒有拋出OutOfMemoryError異常,測試環境jdk1.8, 開發工具IDEA, 目前還不知道為什么,如果有哪位朋友知道為什么,大家一起討論下
結構梳理
引用參考
- 深入理解Java虛擬機-周志明 著