自動記憶體管理機制_執行時資料區域

z1340954953發表於2018-03-28

程式計數器

一塊記憶體區域,可以看做是當前執行緒執行的位元組碼的行號指示器。在虛擬機器的概念模型中,位元組碼直譯器工作時候,就是通過改變這個序號來執行位元組碼指令,比如分支,迴圈,跳轉,異常處理,執行緒恢復等基礎功能都需要。

由於java虛擬機器的多執行緒是通過執行緒輪流切換執行的。任意一個時刻只能執行一個執行緒,因此為了每個執行緒切換後都能恢復到原來執行的位置,那麼每個執行緒都必須獨有一個程式計數器。各個計數器鍵互不影響,這類記憶體區域稱為"執行緒私有"記憶體

當執行是一個java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是native方法,這個計數器則是空。

此記憶體區域是唯一一個在java虛擬機器規範中沒有規定任何outOfMemoryError情況的區域

虛擬機器棧

同樣java虛擬機器棧也是每個執行緒私有的。它的生命週期和執行緒相同。

虛擬機器棧用在描述java方法執行的記憶體模型:每個方法 在執行中會建立一個棧幀(存放區域性變數表,運算元棧,動態連結,方法出口等資訊)。每個方法從呼叫到執行完畢的過程,對應一個棧幀在虛擬機器中入棧道出棧的過程

棧幀中存放的區域性變數表是什麼?

區域性變數表存放了編譯器可知的各種基本資料型別,物件引用哦(不是物件本身,而是指向物件的地址)和returnAddress型別(指向了一條位元組碼指令的地址)

注意:

1. 64位的long和double型別的資料會佔有2個區域性變數空間,其餘的資料型別只會佔有一個。

2. 區域性變數表佔用的空間大小在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的區域性變數空間是完全確定的。在方法執行間不會改變區域性變數表的大小

3. 在java虛擬機器規範中,對這個區域規定了兩種異常情況:如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲Stack Overflow異常;如果虛擬機器可以動態擴充套件,擴充套件無法申請到足夠的記憶體,會丟擲異常OutOfMemoryError

本地方法棧

和java虛擬機器棧類似,不過虛擬機器棧為java方法服務,本地方法棧為native方法服務(native其他非java語言實現的方法)

Java堆

Java堆是java虛擬機器所管理的記憶體區域中最大的一塊。Java堆是被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時建立。此記憶體區域的唯一目的,就是存放物件例項,幾乎所有的物件例項/陣列都在這裡分配記憶體.

Java堆死垃圾收集器管理的主要區域,因而也被稱為是GC堆。

Java堆按照分代收集演算法可以分為新生代和老年代,從記憶體分配的角度來看,執行緒共享的java對可能劃分出多個執行緒私有的分配緩衝區,不管怎麼劃分,儲存的仍是java例項物件

方法區

方法區(Method Area)與Java堆一樣,是各個執行緒共享的記憶體區域,它用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。雖然Java虛擬機器把方法區描述為堆的一個邏輯部分,一般稱為非堆,就是要和java堆進行區分。

需要注意的是:

Java虛擬機器規範對方法區的限制非常寬鬆,除了和java堆一樣不需要連續的記憶體和可以選擇固定大小或者可擴充套件外,還可以選擇不實現垃圾收集。相對而言,垃圾收集行為在這個區域是比較少出現的,但是並非資料進入了方法區就如永久代的名字一樣"永久"存在了。這區域的記憶體回收目標主要是針對常量池的回收和對型別的解除安裝,一般來說,這個區域的回收成績比較難以令人滿意。

執行時常量池

執行時常量池是方法區的一部分。Class檔案除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池,用於存放編譯器生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中儲存。

執行時常量池相對於Class檔案常量池的另外一個重要特徵是具備動態性,java語言並不要求常量一定只有編譯器才能產生,也就是並非預置進Class檔案中的常量池的內容才能進入方法區執行時常量池,執行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法

執行時常量池是方法區的一部分,自然受到方法區記憶體的限制,當常量池無法在申請到記憶體時會丟擲OutOfMemoryError異常。

直接記憶體

直接記憶體(Direct Memory)並是不虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域。但是也可能導致OutofMemoryError異常出現。

在jdk1.4中加入了NIO類,引入一種基於通道和緩衝區的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在Java堆中的DirectByteBuffer物件作為這個記憶體的引用進行操作,避免在Java堆和Native堆中來回複製資料。

注意:

1. 本機直接記憶體的分配不會受到java堆大小的限制,但是回受到本機總記憶體的大小和處理器定址空間的限制。

2. 配置虛擬機器引數時,會根據實際記憶體設定-Xmx等引數資訊,但是不要忽視直接記憶體,是的記憶體區域總和大於實體記憶體

---------------------------------------------------------------------------------------------------------------------------

部落格內容來自:周志明<<深入理解java虛擬機器-JVM高階特性與最佳實踐(第二版)>>


相關文章