Java虛擬機管理的內存包括如圖所示的運行時數據區域:
下面分別進行介紹:
1)程序計數器(Program Counter Register)
- 佔用的內存空間比較小,主要作用就是標識當前線程執行的字節碼的行號。字節碼解釋器的工作就是通過不斷改變計數器的值來獲取下一條要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等功能都需要依賴程序計數器。
- 程序計數器在每個線程中都是獨立互不影響的,因爲Java虛擬機多線程的實現是通過輪流切換並分配時間給每個線程來完成的。每個處理器在一個確定的時刻只會執行一條線程中的指令,切換線程之後需要恢復到正確的執行位置。
- 計數器值的含義:如果正在執行的是Java方法,記錄的是字節碼指令的地址;如果執行的是Native方法,則計數器值爲空(Undefined),該內存區域是唯一一個在虛擬機規範中沒有規定任何OutOfMemoryError的區域。
2)Java虛擬機棧(Java Virtual Machine Stacks)
- 線程私有,生命週期與線程相同。
- 含義:描述的是Java方法執行的內存模型。每個方法在執行時會創建一個棧幀(Stack Frame)用來存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。方法從調用開始到執行結束的過程就是棧幀在虛擬機棧中入棧到出棧的過程。
- 局部變量表存放的是int等各種基本數據類型、對象引用以及指向下一條執行地址的returnAddress類型等。
- 64位長度的long和double類型的數據會佔用2個局部變量空間(Slot),其餘的類型只佔1個。
- 方法運行期間不會改變局部變量表的大小。
- 兩種異常情況:StackOverflowError(線程請求的棧深度大於虛擬機允許的深度);OutOfMemoryError(虛擬機動態擴展時無法申請到足夠的內存)。
3)本地方法棧(Native Method Stack)
- 與虛擬機棧功能類似,區別:虛擬機棧執行的是Java(字節碼)服務;本地方法棧爲Native方法服務。
- 拋出的異常:StackOverflowError和OutOfMemoryError。
4)Java堆(Java Heap)
- 所有線程共享,虛擬機啓動時創建;
- 用來存放對象的實例:所有的對象實例和數組都要在堆上分配;
- 還可以細分:新生代、老年代;Eden、From Survivor、To Survivor;
- 堆可以處於物理上不連續的內存空間中;
- 異常:OutOfMemoryError,對重沒有內存來完成實例分配並且也不能再擴展時。
5)方法區(Method Area)
- 各線程共享;
- 存儲內容:已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等。
- 被稱爲永久代(但是不嚴格。。)。
- 限制比較寬鬆:不需要連續的內存、可以選擇固定大小或者可擴展、可以不實現垃圾收集。
- 該區域內存回收的目標:針對常量池的回收和對類型的卸載。
- 異常:OutOfMemoryError,該區域無法滿足內存分配需求時。
6)運行時常量池(Runtime Constant Pool)
- 方法區的一部分;
- Class文件內容:類的版本、字段、方法、接口等描述信息和常量池(Constant Pool Table);
- 常量池作用:存放編譯期生成的各種字面量和符號引用。
- 動態性:Java並不要求常量一定要在編譯器才能產生,運行期間也可以將新的常量放入池中,應用--String.intern()方法
- 異常:OutOfMemoryError,運行時常量是方法去的一部分,類似,無法申請到內存時就拋出異常。
7)直接內存(Direct Memory)
- 不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。
- JDK1.4中:引入了NIO(New Input/Output),可以使用Native函數庫直接分配堆外內存,然後通過存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用來進行操作,避免了再Java堆和Native堆中來回複製數據。