Java JVM詳解內存模型以及分區

Java內存模型即Java Memory Model(JMM)。JVM是整個計算機虛擬模型,JMM定義了JVM在計算機內存(RAM)中的工作方式,所以JMM是隸屬于JVM的。

java 文件被 JVM 加載到內存中的過程:

  1. HelloWorld.java文件首先需要經過編譯器編譯,生成HelloWorld.class字節碼文件。
  2. Java 程序中訪問HelloWorld這個類時,需要通過 ClassLoader(類加載器)將HelloWorld.class 加載到 JVM 的內存中。
  3. JVM 中的內存可以劃分為若干個不同的數據區域,主要分為:程序計數器、虛擬機棧、本地方法棧、堆、方法區

java代碼的編譯和執行過程


加載流程

class 文件被加載到內存中步驟
  1. 裝載:指查找字節流,并根據此字節流創建類的過程。裝載過程成功的標志就是在方法區中成功創建了類所對應的 Class 對象。

  2. 鏈接:指驗證創建的類,并將其解析到 JVM 中使之能夠被 JVM 執行。

  • 驗證:檢查讀入結構是否符合JVM規范的描述(文件格式檢驗、元數據、字節碼、符號引用檢驗)
  • 準備:為類中的靜態變量分配內存,并為其設置“0值”
  • 解析:把常量池中的符號引用轉換為直接引用,也就是具體的內存地址。在這一階段,JVM 會將常量池中的類、接口名、字段名、方法名等轉換為具體的內存地址。
  1. 初始化:則是將標記為 static 的字段進行賦值,并且執行 static 標記的代碼語句 。

jvm內存分區

  • 程序計數器
  • Java虛擬機棧
  • 本地方法棧
  • Java堆
  • 方法區

程序計數器

線程私有,是虛擬機中一塊較小的內存空間,用于記錄當前線程執行的位置。
此內存區域是唯一一個在JVM規范中沒有規定任何OutofMemoryError情況的區域

作用:Java程序是多線程的,CPU可以在多個線程中分配執行時間片段。當某一個線程被CPU掛起時,需要記錄代碼已經執行到的位置,方便CPU重新執行此線程時,知道從哪行指令開始執行。

可以看成是當前線程所執行的字節碼的行號指示器。通過改變這個計數器的值來選取下一條需要執行的字節碼指令。分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴計數器。

注意事項:

1.在 Java 虛擬機規范中,對程序計數器這一區域沒有規定任何 OutOfMemoryError 情況。

  1. 程序計數器是線程私有的,每條線程內部都有一個私有程序計數器。它的生命周期隨著線程的創建而創建,隨著線程的結束而死亡。

  2. 當一個線程正在執行一個 Java 方法的時候,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址。如果正在執行的是 Native 方法,這個計數器值則為空(Undefined)。


Java虛擬機棧

作用:存放java方法執行時的所有數據
組成:由棧幀組成,一個棧幀代表一個方法的執行

線程私有,生命周期與線程相同。
異常StackOverflowError和OutOfMemoryError

  • StackOverflowError:線程請求棧深度超出虛擬機棧所允許的深度。
    (遞歸調用可能導致)
  • OutOfMemoryError:java虛擬機動態擴展無法申請足夠內存時拋出
    (虛擬機棧、堆、方法去都有可能出現,大多數出現在堆中)

虛擬機棧描述的是Java方法執行的內存模型,每個方法執行時jvm都會在虛擬機棧中創建一個棧幀。

棧幀

  • 用于支持虛擬機進行方法調用和執行的數據結構。
  • 每個線程執行某個方法時,都會為這個方法創建一個棧幀。
  • 每個方法從調用到執行完成,就對應一個棧幀在虛擬機棧中的入棧和到出棧的過程。

一個線程包括多個棧幀,每個棧幀內部包含局部變量表、操作數棧、動態鏈接、返回地址等。

線程&棧幀

局部變量表:變量值的存貯空間。調用方法傳遞的參數、方法內部的局部變量都在這里。
操作數棧:也稱作操作棧。后入先出棧。
動態鏈接:支持方法調用過程中的動態連接。
返回地址:無論方法是否正常退出,在方法退出后都需要返回到方法被調用的位置,程序才能正常值行。返回地址就是幫助方法回復他的上層方法執行狀態。


本地方法棧

功能與虛擬機棧類似。區別在于本地方法棧為native方法服務。


Java堆

JVM所管理的內存中最大的一塊,用來存放對象實例,被所有線程所共享

幾乎所有對象實例都在堆中進行分配,因此時垃圾收集器(GC)管理的主要區域。

可分為:新生代和老年代。新生代可再細分為:Eden空間、From Survivor空間、To Survivor空間。有OutOfMemoryError異常。


方法區

  • 存儲被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后等數據。運行時常量池是方法區一部分。(常量池、源代碼中的命名常量、String常量,Static都保存在方法區)
  • 跟Java堆一樣,是各個線程共享區域。
  • 是規范層面的東西,規定了這一個區域要放哪些數據。


直接內存

不屬于虛擬機運行時數據區的一部分。Java NIO引入了一種基于通道與緩沖區的IO方式。可以使用Native函數庫直接分配堆外內存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。避免Java堆和Native堆之間來回復制數據,在某種場景中顯著提高性能。由于不在堆中分配,因此不受到堆大小限制。但既然是內存總有會被用完時候,因此會拋出OutOfMemoryError。


Dalvik與JVM的不同

  • 執行文件不同,一個是dex,一個是class
  • 類加載系統與jvm區別較大
  • 可以同時存在多個DVM
  • Dalvik基于寄存器,JVM基于棧(寄存器是比內存更快的存儲介質)

ART比Dalvik優勢

  • DVM使用JIT來將字節碼轉換成機器碼,效率低
  • ART采用了AOT預編譯技術,執行速度更快(安裝時字節碼轉換成機器碼)
  • ART會占用更多的應用安裝時間和存儲空間
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容