本文爲 《深入理解Java虛擬機》第二章內容的學習筆記,部分內容經過二次加工。若對相關知識感興趣,推薦購書深入閱讀。若認爲文章涉嫌侵權,請聯繫作者及時刪除。
本作品採用 知識共享署名-非商業性使用-相同方式共享 3.0 中國大陸許可協議 (CC BY-NC-SA 3.0 CN) 進行許可 。非商業性質轉載請註明作者和出處,禁止商業性質轉載。
開源創造世界
個人練習代碼:https://github.com/dreamerfable/Understanding-the-JVM
運行時數據區域
根據 JVM規範,Java虛擬機鎖管理的內存包括以下幾個運行時區域
各線程獨立存儲,互不影響的內存,稱作線程私有的內存
Program Counter Register
程序計數器是一塊較小的內存空間,可以看做是當前線程所執行的字節碼的行號指示器,分支、循環、跳轉、異常處理等基礎功能都需要依賴它完成
線程私有
這個區域不存在OutOfMemoryError情況
Virtual Machine Stack
虛擬機棧描述的是Java方法執行的內存模型,每個方法在執行的同時都會創建一個棧幀 Stack Frame,用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法調用直至執行完成的過程,對應着一個棧幀在虛擬機棧中入棧到出棧的過程。
線程私有
如果線程請求的棧深度大於虛擬機所允許的深度,拋出StackOverflowError
如果虛擬機棧可以動態擴展,擴展時如果無法申請到足夠的內存,拋出OutOfMemoryError異常
Native Method Stack
本地方法棧與VM Stack作用類似。不同的是,VM Stack 爲虛擬機執行Java方法服務,而本地方法棧爲虛擬機使用到的Native方法服務。
虛擬機規範對本地方法棧沒有強制規定,部分虛擬機把本地方法棧與虛擬機棧合二爲一,如HotSpot
會拋出StackOverflowError和OutOfMemoryError。
Heap
堆,是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。用於存放對象實例,幾乎所有對象實例都在這裏分配內存。
堆是垃圾收集器管理的主要區域。從內存回收角度看,由於現在收集器基本都採用分代收集算法,所以堆可以細分爲:新生代、老年代;再細緻可以分爲Eden空間、From Survivor空間、To Survivor空間等。
在堆中沒有內存完成實例分配,並且堆也無法再擴展時,拋出OutOfMemoryError異常。
Method Area
方法區也是被所有線程共享的內存區域,用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等。
JVM規範將其描述爲堆的一個邏輯部分,但其別名爲 Non-Heap,用來將其與堆加以區分。
根據JVM規範,當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。
Runtime Constant Pool
運行時常量池用於存放Class文件中的常量池
常量池 Constant Pool Table 在Class文件中用於存放編譯期生成的各種字面量和符號引用,這部分內容在類加載後會進入Method Area的運行時常量池。
運行時常量池除了保存Class文件中描述的符號引用外,還會把翻譯出來的直接引用也存儲下來。另外,運行時常量池相比較於Class文件常量池具有動態性,可以保存運行時產生的常量。
無法再申請到內存時拋出OutOfMemoryError異常。
Direct Memory
直接內存 不是虛擬機運行時數據區的一部分,也不是JVM規範中定義的內存區域。但這部分同樣被頻繁的使用,而且也可能導致OutOfMemoryError異常出現。
JDK1.4中新加入的NIO類,引入了一種基於通道 Channel 與 緩衝區 Buffer 的 I/O方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因爲避免了在Java堆和Native堆中來回複製數據。
本季直接內存的分配不會受到Java堆大小的限制,但是其受到本機總內存大小以及處理器尋址空間的限制,可能會導致動態擴展時出現OutOfMemoryError異常。