1. 程式人生 > >Java虛擬機:內存模型詳解

Java虛擬機:內存模型詳解

調用 數據類型 可擴展 空間 共享 sof 虛擬機 進程 部分

版權聲明:本文為博主原創文章,轉載請註明出處,歡迎交流學習!

我們都知道,當虛擬機執行Java代碼的時候,首先要把字節碼文件加載到內存,那麽這些類的信息都存放在內存中的哪個區域呢?當我們創建一個對象實例的時候,虛擬機要為對象分配內存,Java虛擬機又是如何配分內存的呢?這些都涉及到Java虛擬機的內存劃分機制,今天我們就來探究一下Java虛擬機的內存模型。

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若幹個不同的數據區域,這些區域都有各自的用途以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而存在,有些區域則依賴用戶線程的啟動和結束而建立和銷毀。根據Java虛擬機規範的規定,Java虛擬機所管理的內存包括以下幾個數據區域。如下圖所示:

技術分享

以上就是Java虛擬機運行時數據區域的劃分,每一塊內存區域都有它的職責,存放著不同的運行時數據。

虛擬機棧

Java虛擬機棧是線程私有的,每一個線程在這個區域都有一塊所屬的內存區域,它的生命周期與線程相同,隨線程啟動而生,隨線程消亡而滅。虛擬機棧描述的是Java方法執行的內存模型,每一個線程都對應著虛擬機棧區域裏的一個棧數據結構,由於一個線程的方法調用鏈可能會很長,每一個方法在執行時都會創建一個棧幀,棧幀就是線程對應的棧數據結構的棧元素,棧幀用於存儲局部變量表、操作數棧、動態鏈接等信息。局部變量表存放了方法參數和方法內部定義的局部變量,包括各種基本數據類型和對象引用類型等信息。經常聽到有的程序猿粗糙的把虛擬機內存劃分為堆內存和棧內存,這種劃分只能說明大多數程序猿比較關註的、與對象內存分配關系最密切的是這兩塊內存區域,其中的“棧內存”就是這裏所說的虛擬機棧,而虛擬機棧裏我們程序猿最為關註的就是局部變量表部分。

本地方法棧

本地方法棧也是線程私有的,它與虛擬機棧發揮的作用相似,它們之間的區別不過是虛擬機棧為虛擬機執行Java方法服務,而本地方法棧則為虛擬機使用到的Native方法(本地方法)服務。在Java虛擬機規範中並沒有對本地方法棧的實現做強制規定,有的虛擬機甚至直接把虛擬機棧和本地方法棧合二為一。

Java堆是所有線程所共享的一塊內存區域,也是Java虛擬機所管理的內存中最大的一塊。這塊內存的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。Java堆是垃圾收集器管理的主要區域,從內存回收的角度看,由於現在收集器基本都采用分代收集算法,所以Java堆還可以細分為新生代和老年代。新生代還可以再細分為Eden區域、FromSurvivor區域和ToSurvivor區域。無論怎麽劃分,都與存放的內容無關,存儲的任然都是對象實例。進一步劃分的目的是為了更好的回收或者更快的分配內存。

方法區

方法區與Java堆一樣,是線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、編譯器編譯後的代碼等數據。方法區是一個邏輯區,具體屬於哪一塊物理內存根據不同的虛擬機實現而定。在HotSpot的實現中,方法區邏輯上與堆內存隔離,物理存儲上卻是是屬於Java堆的一部分。很多人把方法區稱為“永久代”,其實是HotSpot使用永久代來實現方法區而已。其他的虛擬機實現並沒有永久代這一概念。Java虛擬機規範對方法區的限制非常寬松,除了和Java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以選擇不實現垃圾回收。一般來說不會在方法區進行垃圾回收,在這一區域進行回收的效果很難讓人滿意。當方法區無法滿足內存分配需求時會拋出內存溢出異常。

運行時常量池是方法區的一部分,用於存放編譯期間生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存儲。

程序計數器

程序計數器是一塊很小的內存空間,它也是線程私有的。它可以看作是當前線程所執行的字節碼的行號指示器,通過改變這個計數器的值來選取下一行需要執行的字節碼指令。由於Java虛擬機的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器內核只會執行一條線程中的指令,因此,為了線程切換後能夠恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,各條線程之間互不影響,獨立存儲。

以上就是Java虛擬機的內存模型劃分,這是我們程序猿必須掌握的原理,弄清Java虛擬機的內存模型,是理解虛擬機內存分配和垃圾回收的基礎,以此作為總結。

Java虛擬機:內存模型詳解