首頁  >  文章  >  Java  >  Java虛擬機器中記憶體管理的深入解析

Java虛擬機器中記憶體管理的深入解析

不言
不言原創
2018-09-12 15:17:041596瀏覽

這篇文章帶給大家的內容是關於Java虛擬機中記憶體管理的深入解析 ,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

執行階段記憶體包括:

  1. 方法區(Method Area)

  2. ##虛擬機棧(VM Stack)

  3. 本機方法堆疊(Native Method Stack)

  4. ##堆疊( Heap)
  5. 程式計數器(Program counter Register)
  6. 程式計數器

是一塊比較小的記憶體空間,可以看做是目前執行緒所執行的字節碼的行號指示器。 (字節碼解釋器工作時是透過改變這個計數器的值來選取嚇一跳需要執行的字節碼指令)。

由於Java虛擬機器的多執行緒是透過執行緒輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器都只會執行一條執行緒中的指令。因此,為了線程切換後能回復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立儲存,我們城這類內存區域為“線程私有”的內存。

如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器字節碼指令的位址,如果是native方法,這個計數器值則是空。

Java虛擬機器堆疊

Java虛擬機器堆疊也是執行緒私有的,生命週期與執行緒相同。

虛擬機棧描繪的是執行的記憶體模型:每個方法在執行的同時會建立一個堆疊幀用於儲存局部變數表,操作數棧,動態連結、方法出口等資訊。每一個方法從呼叫直到執行完成的過程,就對應一個堆疊幀在虛擬機器中入棧到出棧的過程。

一般指的堆疊就是講的虛擬機棧,或者說是虛擬機器堆疊中局部變數表部分。

局部變數表存放了編譯期可知的各種基本資料類型,物件參考和returnAddress類型(指向了一條字節碼指令的位址)。

其中long和double類型的資料會佔用兩個局部變數空間(Slot),其餘資料型別只佔一個。

局部變數表所需的記憶體空間在編譯期完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部標量空間是完全確定的,在方法運行期間不會改變局部變數表的大小。

虛擬機器規範中對這個區域規定了兩種異常狀況:如果執行緒請求的堆疊深度大於虛擬機器所允許的深度,將拋出StackOverflowError異常;如果虛擬機器堆疊可以動態擴展,如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。

本地方法堆疊

與虛擬機器堆疊的作用十分相似,本地方法堆疊是為虛擬機器所使用到的Native方法所服務。對本地方法中的語言,使用方式和資料結構沒有強制規定。

Java堆

對大多數應用程式來說,Heap是虛擬機器所管理的記憶體中最大的一塊。

堆是所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時創建。此記憶體區域的唯一目的是存放物件實例,幾乎所有物件實例都是在這裡分配記憶體(所有物件實例和陣列)。

Java堆是垃圾收集器管理的主要區域。因現收集器基本上都採用分帶收集方法,所以Java堆中還可以細分為:新生代和老生代。

從記憶體分配的角度,在執行緒共享的Java堆中可能會分割出多個執行緒私有的分配緩衝區(Thread Local Allocation Buffer, TLAB)。

Java堆可以處理物理上不連續的記憶體空間,只要邏輯上是連續的即可。在實現時,可以實現成固定大小的,也可以可拓展的,不過主流的虛擬機都是按可拓展實現(-Xms、-Xmx控制)。

方法區

與Java堆一樣,是各個執行緒共享的記憶體區域,它用來儲存已經被虛擬機器載入的類別資訊、常數、靜態變數、即時編譯器編譯後的代碼等數據。雖然Java虛擬機器把方法區描述為堆的一個邏輯部分,但是它卻有一個名叫做Non-Heap,目的在於與Java堆區分開。

除了和Java堆一樣不需要連續的記憶體和可以選擇固定大小或可拓展外,還可以選擇不實現垃圾回收。垃圾回收在這個區域內很少發生,但並非進入了方法區就如永生代一樣永久存在了。這個區域的記憶體回收主要是針對常量池的回收和對類型的卸載。

運行時常數池

是方法區的一部份。 class檔案中除了有類別的版本、欄位、方法、介面等資訊外。還有一個資訊是常數池(Constant Pool Table),用於編譯期產生的各種字面量和符號引用,這部分內容將在類別載入後進入方法區的執行時間常數池中存放。

具備動態性,並非預先置入Class文件中常量池的內容才能進入方法區運行時常量池,運行期也可能將新的常量放入池中,這種特性被開發人員利用的比較多的是String類別的intern().

物件的建立

虛擬機器接到一條new指令時,首先去檢查這條指令的參數是否能在常數池中定位到一個類別的符號引用,並且檢查這個符號引用代表的類別是否已經被載入、解析和初始化過。如果沒有那麼必須先執行回應的類別載入過程。

在類別載入檢查之後就為新生物件分配記憶體。物件所需記憶體大小在類別載入完成後便可完全確定。

記憶體分配完成之後,虛擬機器需要分配到的記憶體空間都初始化為零,如果使用TLAB,這項工作可以提前至TLAB分配時進行。

接下來虛擬機器對物件進行必要的設置,例如這個物件是 哪個類別的實例,如何才能找到類別的元資料資訊、物件的雜湊碼、物件的GC分代年齡等資訊。這些資訊放在物件的物件頭中。

完成上步之後,對於虛擬機器之後已經完成任務了,但從Java程式的角度來看,物件建立才剛開始,還沒執行。

物件的記憶體佈局

物件內存在記憶體中的佈局可以分為3部分:物件頭、實例資料和對齊填滿。

HotSpot虛擬機的物件頭包括兩部分:

1.儲存物件本身的執行時間數據,如雜湊嗎、GC分代年齡、鎖定狀態標誌、執行緒持有鎖、偏向線程ID、偏向時間戳記等。資料長度在32位元和64位元虛擬機器分別為32bit和64bit,官方成為「Mark word」。

2.型別指針,也就是物件之鄉它的類別元資料的指針,虛擬機器透過這個指針來決定這個物件是哪個類別的實例。如果物件是一個Java數組,拿在物件頭中還必須有一塊用於記錄數組長度的數據,因為虛擬機器可以透過普通Java物件的元資料來決定Java物件大小,但從數組的元資料中卻無法確定數組的大小。

物件的存取定位(P48)

Java程式堆疊上的reference資料可以透過以下兩種主流方式存取堆疊中的物件實例。

句柄:Java堆中會分配一塊記憶體用來當句柄吃,reference儲存的就是這個物件的句柄位址。句柄中包含了物件範例資料與型別資料各自的具體體質。

直接指標:Java堆物件佈局中就必須考慮如何防止存取類型資料的相關資訊。

相關推薦:

Java虛擬機器管理的記憶體運行時資料區域的詳細介紹

圖文詳解JAVA虛擬機器相關知識-JVM記憶體模型

#

以上是Java虛擬機器中記憶體管理的深入解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn