JVM——JVM內存結構

重點

堆、棧、方法區、直接內存、堆和棧區別。

概要

對於Java程序員來說,內存的管理都是交由JVM內存管理機制來控制,雖然美好,但是一旦出現內存泄漏和溢出的問題,不瞭解虛擬機是怎麼樣使用內存的話,排查起來將會一件很棘手的事情。

運行時數據區域

Java虛擬機所管理的內存包含以下幾個運行時區域:
在這裏插入圖片描述
(圖片來源:https://www.cnblogs.com/ityouknow/p/5610232.html)
這些區域有着各自的用途,有的區域會隨着虛擬機進程的啓動二創建,而有的區域會隨着用戶線程的啓動和結束進行創建和銷燬。

程序計數器

程序計數器是一塊比較小的內存區域,可以當成是當前線程執行字節碼的行號指示器。字節碼解釋器就是通過改變這個計數器的值來取下一條需要執行的指令。
程序計數器是線程私有的。因爲Java虛擬機的多線程實現是,通過線程的輪流切換並且分配處理器執行時間,在任意確定的時刻,一個處理器(多核處理器是指一個內核)都只會執行一個線程中指令,爲了線程切換後能夠恢復到正確的執行位置,每個線程都要有自己獨立的程序計數器,線程之前不能相互影響,獨立存儲的。
如果虛擬機執行的是一個Java方法,則計數器記錄的是虛擬機字節碼指令的地址。如果執行的是native方法,計數器是空的。該區域也是Java虛擬機規範唯一沒有規定OutOfMemotyError情況的區域。

Java虛擬機棧

該區域同樣是線程私有的。虛擬機棧描述的是Java方法內存模型。
每個方法在執行時,會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈接和方法出口等信息。每一個方法從調用到結束,對應着棧幀入棧到出棧的過程。
局部變量表
局部變量表存放着編譯期可知的基本數據類型(boolean、byte、char、short、int、long、float和double)、對象引用和returnAddress(指向一條字節碼指令的地址)。其中double和long會佔用2個局部變量空間,其餘數據類型佔用1個。局部變量表所需的內存在編譯期間完成了分配,並且在方法運行時不會改變其大小。
異常狀況
StackOverflowError:當線程申請的棧深度大於虛擬機所允許的深度,拋出該異常。
OutOfMemoryError:虛擬機棧可以動態擴展,如果擴展無法申請足夠的內存,拋出該異常。

本地方法棧

本地方法棧和Java虛擬機棧發揮作用相似,只不過本地方法棧爲虛擬機使用native方法服務,而Java虛擬機棧爲Java方法服務。異常情況和Java虛擬機棧相同。

Java堆

Java堆是虛擬機管理內存的最大一塊區域。這塊區域隨着虛擬機的啓動而創建,因此,這塊區域是所有線程共享的一塊區域。
Java堆保管着Java所有的對象實例和數組。(但是隨着JIT編譯器發展和逃逸分析技術的成熟,這個“所有對象”不再那麼絕對)
Java堆劃分
Java對是垃圾收集的主要區域。從內存回收的角度看,由於現在垃圾收集器大部分採用分代收集算法,因此Java堆可以分爲:新生代和老年代,再細緻一點就是:Eden空間、From Survivor空間和To Survivor空間。從內存分配的角度看,因爲Java堆是線程共享的,所以Java堆可能劃分出多個線程私有的分配緩衝區(TLAB)。無論如何劃分,Java堆存儲的都是Java對象實例,劃分的目的在於更好回收內存或者更高效的分配內存。
異常情況
Java堆是邏輯上的連續空間,即物理上可以不連續。在實現上,既可以實現成固定的大小,也可以實現成可擴展的(-Xmxh和-Xms)。如果Java堆沒有足夠大內存完成實例的分配並且堆也無法擴展,拋出OutOfMemoryError。

方法區

方法區是線程共享的區域。用來存儲虛擬機加載的類的信息、靜態變量、常量和即時編譯器編譯後的代碼數據。
虛擬機對方法區的限制比較鬆,內存可以不物理連續,可以選擇固定大小或者可以擴展的,可以選擇不進行垃圾回收。垃圾回收在方法區很少出現,但是不代表方法區的數據就是“永久代”,方法區垃圾回收主要是常量池的回收和類型的卸載。
當方法區無法滿足內存分配需求時,拋出OutOfMemoryError異常。

運行時常量池

運行時常量池是方法區的一部分。Class文件除了類的版本、字段、方法和接口信息外,還有一個信息項就是常量池,保存編譯時期生成字面量和符號應用,這部分內容在類加載完成後進入到運行時常量池,除了Class文件描述的符號引用外,翻譯出來的直接引用也會保存在運行時常量池。
運行時常量池具備動態性,這是區別Class文件常量池的一個重要特徵,即運行時常量池在運行期間也可以將新的常量保存,比如String的intern方法。
當常量池無法申請到內存時,拋出OutOfMemoryError異常。

直接內存

直接內存不是運行時區域的部分,也不是Java虛擬機規範定義的內存區域,但是直接內存也會被頻繁的使用,使用不當,會拋出OutOfMemoryError異常。
在JDK1.4中,加入了NIO類,引入了基於通道與緩衝區的I/O方式,它可以使用native函數庫直接分配堆外內存,然後通過存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。因爲直接內存的分配不受Java堆大小的限制,但是會受到本機總內存大小或者處理器尋址空間的限制,如果在配置虛擬機參數時,使得各個內存區域之和大於物理內存的限制,就會導致在動態擴展時出現OutOfMemoryError異常。

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