JVM記憶體模型總結

OkidoGreen發表於2020-04-05

https://blog.csdn.net/u011972171/article/details/80398771

 

JVM記憶體模型

                

從這張圖中很直觀的看到,程式計數器,虛擬機器棧,native棧是執行緒私有的,堆是執行緒共有的,現在詳細介紹JVM各個區塊。

1. 堆(Heap)

       是java虛擬機器所管理的記憶體中最大的一塊記憶體區域,也是被各個執行緒共享的記憶體區域,該記憶體區域存放了物件例項及陣列(但不是所有的物件例項都在堆中)。其大小通過-Xms(最小值)和-Xmx(最大值)引數設定(最大最小值都要小於1G),前者為啟動時申請的最小記憶體,預設為作業系統實體記憶體的1/64,後者為JVM可申請的最大記憶體,預設為實體記憶體的1/4,預設當空餘堆記憶體小於40%時,JVM會增大堆記憶體到-Xmx指定的大小,可通過-XX:MinHeapFreeRation=來指定這個比列;當空餘堆記憶體大於70%時,JVM會減小堆記憶體的大小到-Xms指定的大小,可通過XX:MaxHeapFreeRation=來指定這個比列,當然為了避免在執行時頻繁調整Heap的大小,通常-Xms與-Xmx的值設成一樣。堆記憶體 = 新生代+老生代+持久代。在我們垃圾回收的時候,我們往往將堆記憶體分成新生代和老生代(大小比例1:2),新生代中由Eden和Survivor0,Survivor1組成,三者的比例是8:1:1,新生代的回收機制採用複製演算法,在Minor GC的時候,我們都留一個存活區用來存放存活的物件,真正進行的區域是Eden+其中一個存活區,當我們的物件時長超過一定年齡時(預設15,可以通過引數設定),將會把物件放入老生代,當然大的物件會直接進入老生代。老生代採用的回收演算法是標記整理演算法。(更詳細的內容將後續文章詳細介紹GC回收)

2. 方法區(Method Area)

     方法區也稱"永久代",它用於儲存虛擬機器載入的類資訊、常量、靜態變數、是各個執行緒共享的記憶體區域。預設最小值為16MB,最大值為64MB(64位JVM由於指標膨脹,預設是85M),可以通過-XX:PermSize 和 -XX:MaxPermSize 引數限制方法區的大小。它是一片連續的堆空間,永久代的垃圾收集是和老年代(old generation)捆綁在一起的,因此無論誰滿了,都會觸發永久代和老年代的垃圾收集。不過,一個明顯的問題是,當JVM載入的類資訊容量超過了引數-XX:MaxPermSize設定的值時,應用將會報OOM的錯誤。引數是通過-XX:PermSize和-XX:MaxPermSize來設定的

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

     從JDK7開始移除永久代(但並沒有移除,還是存在),貯存在永久代的一部分資料已經轉移到了Java Heap或者是Native Heap:符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變數(class statics)轉移到了java heap。從JDK8開始使用元空間(Metaspace),元空間的大小受本地記憶體限制,新引數(MaxMetaspaceSize)用於限制本地記憶體分配給類後設資料的大小。如果沒有指定這個引數,元空間會在執行時根據需要動態調整。

 具體的可以檢視這篇文章:https://blog.csdn.net/zhushuai1221/article/details/52122880 (Java 8: 從永久代(PermGen)到元空間(Metaspace))

3.虛擬機器棧(JVM Stack)

     描述的是java方法執行的記憶體模型:每個方法被執行的時候都會建立一個"棧幀",用於儲存區域性變數表(包括引數)、操作棧、方法出口等資訊。每個方法被呼叫到執行完的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程宣告週期與執行緒相同,是執行緒私有的。棧幀由三部分組成:區域性變數區、運算元棧、幀資料區。區域性變數區被組織為以一個字長為單位、從0開始計數的陣列,和區域性變數區一樣,運算元棧也被組織成一個以字長為單位的陣列。但和前者不同的是,它不是通過索引來訪問的,而是通過入棧和出棧來訪問的,可以看作為臨時資料的儲存區域。除了區域性變數區和運算元棧外,java棧幀還需要一些資料來支援常量池解析、正常方法返回以及異常派發機制。這些資料都儲存在java棧幀的幀資料區中。

    區域性變數表: 存放了編譯器可知的各種基本資料型別、物件引用(引用指標,並非物件本身),其中64位長度的long和double型別的資料會佔用2個區域性變數的空間,其餘資料型別只佔1個。區域性變數表所需的記憶體空間編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的區域性變數是完全確定的,在執行期間棧幀不會改變區域性變數表的大小空間

 

4.本地方法棧(Native Stack)

       與虛擬機器棧基本類似,區別在於虛擬機器棧為虛擬機器執行的java方法服務,而本地方法棧則是為Native方法服務。(棧的空間大小遠遠小於堆)

 

5.程式計數器(PC Register)

   最小的一塊記憶體區域,它的作用當前執行緒所執行的位元組碼的行號指示器,在虛擬機器的模型裡,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令分支迴圈、異常處理、執行緒恢復等基礎功能都需要依賴計數器完成。

 

6.直接記憶體

    直接記憶體並不是虛擬機器記憶體的一部分,也不是Java虛擬機器規範中定義的記憶體區域。jdk1.4中新加入的NIO,引入了通道與緩衝區的IO方式,它可以呼叫Native方法直接分配堆外記憶體,這個堆外記憶體就是本機記憶體,不會影響到堆記憶體的大小.

 

相關文章