深入理解Java虛擬機讀書筆記 - Java運行時數(shù)據(jù)區(qū)域

運行時數(shù)據(jù)區(qū)域


Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存區(qū)域劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而存在,有些區(qū)域則依賴用戶線程的啟動和結(jié)束而建立和銷毀。

程序計數(shù)器


是一塊較小的內(nèi)存空間。它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。

由于Java多線程是通過線程輪流切換并分配處理器執(zhí)行時間來實現(xiàn)的。所以每條線程都有自己獨立的程序計數(shù)器,這樣多線程程序運行時才不會錯亂。所以也稱其為線程“私有”內(nèi)存。

需要注意的是;

  • Java方法:如果當(dāng)前線程正在執(zhí)行的是Java方法。這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址。
  • Native方法:如果執(zhí)行Native方法,則計數(shù)器記錄為空。所以是唯一一個不存在OutOfMemoryError情況的內(nèi)存區(qū)域。

虛擬機棧(重要)


  • 線程私有,生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型,每個方法執(zhí)行都會創(chuàng)建一個棧幀。
  • 棧幀(Stack Frame):用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。局部變量表存放編譯時的8中基本數(shù)據(jù)類型、引用類型和returnAddress類型(指向一條字節(jié)碼指令地址)。
  • 該區(qū)域有兩種異常:
    • 線程請求棧深度大于虛擬機棧深度,拋出StackOverflowError異常。
    • 動態(tài)拓展時無法申請到足夠內(nèi)存,拋出OutOfMemoryError異常。

如果對棧幀所包含的概念不太清楚可以參考:

棧幀、局部變量表、操作數(shù)棧

JAVA內(nèi)存結(jié)構(gòu)之運行時棧幀結(jié)構(gòu)

本地方法棧


與虛擬機棧功能類似,但虛擬機棧為Java方法服務(wù),而本地方法棧為Native方法服務(wù)。也有StackOverflowError和 OutOfMemoryError異常。

Java堆(重要)


  • Java堆(Java Heap)是Java虛擬機管理的最大內(nèi)存區(qū)域,虛擬機啟動時創(chuàng)建,所有線程共享該內(nèi)存。該內(nèi)存唯一目的就是存放對象實例,幾乎所有的對象實例都在此分配內(nèi)存。
  • Java堆是垃圾收集器管理的主要區(qū)域,也被成為“GC堆”。Java堆可被細分為:新生代和老年代。
    • 新生代: 用來存放生命周期較短的對象,而新生代又使用復(fù)制算法進行GC ,又將其按照8:1:1的比例分為一塊較大的Eden空間和2個較小的From Survivor和To Survivor空間。
    • 老年代:用來存放生命周期較長的對象。
  • Java堆可以處于物理上不連續(xù)內(nèi)存區(qū)域,但需要邏輯上連續(xù)。
    • 如果堆中沒有內(nèi)存進行實例分配,并且堆也無法再拓展時(通過-Xmx和-Xms控制),將會拋出OutOfMemoryError異常。
    • Xmx Java堆最大內(nèi)存,默認值為物理內(nèi)存的1/4,當(dāng)可用的Java堆內(nèi)存大于70%時,JVM會將內(nèi)存調(diào)整至-Xms所指定的初始值
    • Xms Java堆初始內(nèi)存,默認值為物理內(nèi)存的1/64,當(dāng)可用的Java堆內(nèi)存小于40%時,JVM會將內(nèi)存調(diào)整至-Xmx所允許的最大值

方法區(qū)(重要)


  • 方法區(qū)和Java堆一樣,是各個線程的共享的區(qū)域,是系統(tǒng)分配的一個內(nèi)存邏輯區(qū)域,它用于存放已被虛擬機加載的類信息(類的描述信息)、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。在HotSpot里也叫“永生代”。但兩者不能等同。如上圖,字符串池在永生代中卻在方法區(qū)外。
  • 可以處于物理上不連續(xù)內(nèi)存區(qū)域,但需要邏輯上連續(xù)。
  • 又叫靜態(tài)區(qū),方法區(qū)包含所有的class和static變量以及常量。
  • 該區(qū)域的垃圾收集比較少見,主要針對常量池的回收和類型的卸載。
  • 方法區(qū)無法滿足內(nèi)存分配需求時,會拋出OutOfMemoryError異常。

運行時常量池

  • 屬于方法區(qū)的一部分。每個類私有
  • 存放 Class 文件中的常量池(存放編譯期生成的各種字面量和符號引用);翻譯出來的直接引用;運行期間產(chǎn)生的新的常量(譬如 String 類的 intern() 方法)。
  • 常量池?zé)o法申請到內(nèi)存, 會拋出OutOfMemoryError異常。

直接內(nèi)存


  • 直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范定義中的內(nèi)存區(qū)域。但這部分區(qū)域被頻繁使用并可能引起OutOfMemoryError異常。
  • NIO(New Input/Output)類中 ,可以使用 Native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在 java 堆里面的 DirectByteBuffer 對象作為這塊內(nèi)存的引用進行操作,避免了在 Java 堆和 Native 堆中來回復(fù)制數(shù)據(jù)。
  • 不受 Java 堆大小的限制,但受本機總內(nèi)存的大小及處理器尋址空間的限制,會拋出 OutOfMemoryError異常。

參考:

漢森X:JVM學(xué)習(xí)01——內(nèi)存區(qū)域及內(nèi)存溢出

付小德:JVM學(xué)習(xí)心得

胖胖:https://www.zhihu.com/question/22739143

Emanue:Java中幾種常量池的區(qū)分

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容