深入理解JVM——Java內存區域與內存溢出異常

本文爲 《深入理解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異常。

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