JVM運行數據區加載.class文件及new一個對象的過程,JVM運行時數據區

JVM創建對象過程

  1. new一個實例,如new People()
  • 到Class文件信息的常量池中檢查是否有People這個類的符號引用,沒有就執行【類加載過程】

  • 爲People對象去堆分配內存(分配的大小已在類加載過程中確定),默認分到Eden區,進行一次GC後沒被回收才轉移到Survivor區。

    • 兩種分配方式:指針碰撞(堆規整,直接內存整移一份當前對象實例大小即可)、空閒列表(堆不規整,用列表記錄那些內存可用)
    • 注意點:帶有壓縮功能的垃圾收集器(如Serial、ParNew)才使java堆規整
  • 爲實例分配內存空間時,通過CAS同步、本地線程緩存這兩種確保線程安全性

  • 內存分配完後,將分配到的內存空間都爲零,不包含對象頭

  • 執行方法,把對象按程序員意願初始化比如構造函數,如private int i = 1;此時就真的把1賦給 i。

2 再說說[加載、驗證、準備、解析、初始化、使用、卸載]七個階段:
2.1 jvm加載.class過程:
1 加載(使用類加載器-詳情見我其他博客的雙親委派模式/當前類加載器/線程上下文類加載器):

  • 1.1 通過類全限定名獲取該類二進制字節流。
  • 1.2 將字節流轉爲方法區的運行時數據結構。
  • 1.3 在方法區生成對應Class對象(存在Class文件信息中),作爲該類的各種數據入口。
    2 驗證:驗證.class文件是否符合jvm要求,如魔數開頭、版本、字節碼、符號引用等(這些均在編譯期已生成)
    3 準備:初始化類變量(如static int i; => 初始化爲0),放到運行時常量池裏,如果定義如下則直接賦好值"5"如下例子:
  • private static final i = 5; 運行時常量池直接給i賦值5。
  • 3.1 將字符串對象的實例引用值存到String Pool。
    4 解析:將常量池中的符號引用轉爲直接引用(該階段也可以是初始化後,如動態綁定),期間會與String Pool作比較
    5 初始化:執行到了階段,初始化靜態變量賦值,然後初始化靜態代碼塊。會先初始化當前類的parent,及最先賦值java.lang.Object的類變量(如果有的話)

Java內存管理:Java內存區域 JVM運行時數據區

Java虛擬機規範定義了字節碼執行期間使用的各種運行時數據區,即JVM在執行Java程序的過程中,會把它管理的內存劃分爲若干個不同的數據區域,包括:

  程序計數器、java虛擬機棧、本地方法棧、java堆、方法區、運行時常量池;

   從線程共享角度來說,可以分爲兩類:

1、所有線程共享的數據區

   方法區、運行時常量池、java堆;

   這些數據區域是在Java虛擬機啓動時創建的,只有當Java虛擬機退出時纔會被銷燬;

2、線程間隔離的數據區

   程序計數器、java虛擬機棧、本地方法棧、

   這些數據區域是每個線程的"私有"數據區,每個線程都有自己的,不與其他線程共享;

   每個線程的數據區在創建線程時創建,並在線程退出時被銷燬;

3、另外,還一種特殊的數據區

  直接內存--使用Native函數庫直接分配的堆外內存;

   即Java內存區域 = JVM運行時數據區 +直接內存。

2、Java各內存區域說明
上面圖片展示的是JVM規範定義的運行時數據概念模型,實際上JVM的實現可能有所差別,下面在介紹各內存數據區時會給出一些HotSpot虛擬機實現的不同點和調整參數。

2-1、程序計數器
程序計數器(Program Counter Register),簡稱PC計數器;

1、生存特點

   每個線程都需要一個獨立的PC計數器,生命週期與所屬線程相同,各線程的計數器互不影響;

2、作用

  JVM字節碼解釋器通過改變這個計數器的值來選取線程的下一條執行指令;

3、存儲內容

   JVM的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的;

   在任意時刻,一個線程只會執行一個方法的代碼(稱爲該線程的當前方法(Current Method));        

(A)、如果這個方法是Java方法,那PC計數器就保存JVM正在執行的字節碼指令的地址;

(B)、如果該方法是native的,那PC計數器的值是空(undefined);

4、內存分配特點

   PC計數器佔用較小的內存空間;

   容量至少應當能保存一個returnAddress類型的數據或者一個與平臺相關的本地指針的值;

5、異常情況

   唯一一個JVM規範中沒有規定會拋出OutOfMemoryError情況的區域;

2-2、Java虛擬機棧
Java虛擬機棧(Java Virtual Machine Stack,JVM Stack),指常說的棧內存(Stack);

   和Java堆指的堆內存(Heap),都是需要重點關注的內存區域;

1、生存特點

   每個線程都有一個私有的,生命週期與所屬線程相同;

2、作用

   描述的是Java方法執行的內存模型,與傳統語言中(如C/C++)的棧類似;

   在方法調用和返回中也扮演了很重要的角色;

3、存儲內容

   用於保存方法的棧幀(Stack Frame);

   每個方法從調用到執行結束,對應其棧幀在JVM棧上的入棧到出棧的過程;

       棧幀:

   每個方法執行時都會創建一個棧幀,隨着方法調用而創建(入棧),隨着方法結束而銷燬(出棧);

   棧幀是方法運行時的基礎結構;

   棧幀用於存儲局部變量表、操作數棧、動態連接、方法出口等信息;

(A)、局部變量表

   局部變量表(Local Variables Table)是一組變量值存儲空間,用於存放方法參數和方法內部定義的局部變量。

   這些都是在編譯期可知的數據,所以一個方法調用時,在JVM棧中分配給該方法的局部變量空間是完全確定的,運行中不改變;

   一個方法分配局部變量表的最大容量由Class文件中該方法的Code屬性的max_locals數據項確定;

(B)、操作數棧

   操作數棧(Operand Stack)簡稱操作棧,它是一個後進先出(Last-In-First-Out,LIFO)棧;

   在方法的執行過程中,會有各種字節碼指令往操作數棧中寫入和提取內容(任意類型的值),也就是入棧/出棧操作;

   在方法調用的時候,操作數棧也用來準備調用方法的參數以及接收方法返回結果;

   一個方法的操作數棧長度由Class文件中該方法的Code屬性的max_stacks數據項確定;

(C)、動態鏈接

   每一個棧幀內部都包含一個指向運行時常量池的引用,來支持當前方法的執行過程中實現動態鏈接 (Dynamic Linking);

   在 Class 文件裏面,描述一個方法調用了其他方法,或者訪問其成員變量是通過符號引用(Symbolic Reference)來表示的;

  動態鏈接的作用就是將這些符號引用所表示的方法轉換爲實際方法的直接引用(除了在類加載階段解析的一部分符號);

4、內存分配特點

   因爲除了棧幀的出棧和入棧之外,JVM棧從來不被直接操作,所以棧幀可以在堆中分配;

   JVM棧所使用的內存不需要保證是連續的;

   JVM規範允許JVM棧被實現成固定大小的或者是根據計算動態擴展和收縮的:

(A)、固定大小

   如果JVM棧是固定大小的,則當創建新線程的棧時,可以獨立地選擇每個JVM棧的大小;

(B)、動態擴展或收縮

   在動態擴展或收縮JVM棧的情況下,JVM實現應該提供調節JVM棧最大和最小內存空間的手段;

兩種情況下,JVM實現都應當提供調節JVM棧初始內存空間大小的手段;

  HotSpot VM通過"-Xss"參數設置JVM棧內存空間大小;

5、異常情況

   JVM規範中對該區域,規定了兩種可能的異常狀況:

(A)、StackOverflowError

   如果線程請求分配的棧深度超過JVM棧允許的最大深度時,JVM將會拋出一個StackOverflowError異常;

(B)、 OutOfMemoryError

   如果JVM棧可以動態擴展,當然擴展的動作目前無法申請到足夠的內存去完成擴展,或者在建立新的線程時沒有足夠的內存去創建對應的虛擬機棧,那JVM將會拋出一個OutOfMemoryError異常;

該區域與方法執行的JVM字節碼指令密切相關,這裏篇幅有限,以後有時間會分析方法的調用與執行過程,再來詳細介紹該區域。

2-3、本地方法棧
本地方法棧(Native Method Stack)與 Java虛擬機棧類似;

1、與Java虛擬機棧的區別

   Java虛擬機棧爲JVM執行Java方法(也就是字節碼)服務;

   本地方法棧則爲Native方法(指使用Java以外的其他語言編寫的方法)服務;

2、HotSpot VM實現方式

   JVM規範中沒有規定本地方法棧中方法使用的語言、方式和數據結構,JVM可以自由實現;

  HotSpot VM直接把本地方法棧和Java虛擬機棧合併爲一個;

2-4、Java堆
Java堆(Java Heap)指常說的堆內存(Heap);

1、生存特點

   所有線程共享;

   生命週期與JVM相同;

2、作用

  爲"new"創建的實例對象提供存儲空間;

   裏面存儲的這些對象實例都是通過垃圾收集器(Garbage Collector)進行自動管理,所以Java堆也稱"GC堆"(Garbage Collected Heap);

對GC堆以及GC的參數設置調整,就是JVM調優的主要內容;

3、存儲內容

  用於存放幾乎所有對象實例;

   (隨JIT編譯技術和逃逸分析技術發展,少量對象實例可能在棧上分配,詳見後面介紹JIT編譯的文章);

4、內存分配特點

(A)、Java堆劃分

   爲更好回收內存,或更快分配內存,需要對Java堆進行劃分:

(I)、從垃圾收集器的角度來看

   JVM規範沒有規定JVM如何實現垃圾收集器;

   由於很多JVM採用分代收集算法,所以Java堆還可以細分爲:新生代、老年代和永久代;

(II)、從內存分配角度來看

   爲解決分配內存線程不安全問題,需要同步處理;

   Java堆可能劃分出每個線程私有的分配緩衝區(Thread Local Allocation Buffer,TLAB),減少線程同步;

HotSpot VM通過"-XX:+/-UseTLAB"指定是否使用TLAB;

(B)、分配調整

   和JVM棧一樣,Java堆所使用的物理內存不需要保證是連續的,邏輯連續即可;

   JVM規範允許Java堆被實現成固定大小的或者是根據計算動態擴展和收縮的:

   兩種情況下,JVM實現都應當提供調節JJava堆初始內存空間大小的手段;

   在動態擴展或收縮的情況下,還應該提供調節最大和最小內存空間的手段;

(C)、HotSpot VM相關調整

   目前主流的JVM都把Java堆實現成動態擴展的,如HotSpot VM:

(1)、初始空間大小

   通過"-Xms"或"-XX:InitialHeapSize"參數指定Java堆初始空間大小;

   默認爲1/64的物理內存空間;

(2)、最大空間大小

   通過"-Xmx"或"-XX:MaxHeapSize"參數指定ava堆內存分配池的最大空間大小;

   默認爲1/4的物理內存空間;

   Parallel垃圾收集器默認的最大堆大小是當小於等於192MB物理內存時,爲物理內存的一半,否則爲物理內存的四分之一;

(3)、各年代內存的佔用空間與可用空間的比例

   通過"-XX:MinHeapFreeRatio"和"-XX:MaxHeapFreeRatio"參數設置堆中各年代內存的佔用空間與可用空間的比例保持在特定範圍內;

   默認:

   "-XX:MinHeapFreeRatio=40":即一個年代(新生代或老年代)內存空餘小於40%時,JVM會從未分配的堆內存中分配給該年代,以保持該年代40%的空餘內存,直到分配完"-Xmx"指定的堆內存最大限制;

   "-XX:MaxHeapFreeRatio=70":即一個年代(新生代或老年代)內存空餘大於70%時,JVM會縮減該年代內存,以保持該年代70%的空餘內存,直到縮減到"-Xms"指定的堆內存最小限制;

  這兩個參數不適用於Parallel垃圾收集器(通過“-XX:YoungGenerationSizeIncrement”、“-XX:TenuredGenerationSizeIncrement ”能及“-XX:AdaptiveSizeDecrementScaleFactor”調節);

(4)、年輕代與老年代的大小比例

   通過"-XX:NewRatio":控制年輕代與老年代的大小比例;

   默認設置"-XX:NewRatio=2"表新生代和老年代之間的比例爲1:2;

   換句話說,eden和survivor空間組合的年輕代大小將是總堆大小的三分之一;

(5)、年輕代空間大小

   通過"-Xmn"參數指定年輕代(nursery)的堆的初始和最大大小;

   或通過"-XX:NewSize"和"-XX:MaxNewSize"限制年輕代的最小大小和最大大小;

(6)、定永久代空間大小

   通過"-XX:MaxPermSize(JDK7)"或"-XX:MaxMetaspaceSize(JDK8)"參數指定永久代的最大內存大小;

   通過"-XX:PermSize(JDK7)"或"-XX:MetaspaceSize(JDK8)"參數指定永久代的內存閾值--超過將觸發垃圾回收;

   注:JDK8中永久代已被刪除,類元數據存儲空間在本地內存中分配;

參考文章:
https://mp.weixin.qq.com/s/rLooaTOU_NQTJdn28KAUFw
https://blog.csdn.net/qq_28666081/article/details/85346470
https://www.cnblogs.com/alsf/p/9484807.html

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