JVM

      網友投稿 862 2025-04-02

      JVM


      PS:JVM部分參考了《深入理解Java虛擬機 - 第二版》(周志明). 個人認為《深入理解Java虛擬機 - 第二版》上的部分內容已經過時 有些知識請各位同學明鑒,此外我后續會根據 《深入理解Java虛擬機 - 第三版》的內容來做更新和修改。

      JVM運行時內存分區

      以HotSpot為例:

      JDK8之前:

      JVM

      線程私有的部分有:程序計數器(PC寄存器),JAVA虛擬機棧,本地方法棧(native)。

      線程共享部分有: GC堆,永久代(是方法區的一種實現)。

      JDK8之后:

      線程私有的部分不變, 線程共享部分的永久代改為了元空間(MetaSpace) (永久代和元空間都是方法區的實現),字符串常量池也移動到了heap空間

      程序計數器是一塊較小的內存空間,它的作用是作為當前線程執行的字節碼的行號計數器。 當字節碼解釋器工作時,通過改變行號計數器的值來選取下一條要執行的字節碼指令。?分支,循環,跳轉,異常處理,線程恢復等功能都需要依賴程序計數器完成。

      程序計數器是屬于線程私有的部分。 當cpu在多個線程之間切換執行時,需要記錄下當前線程執行的字節碼的位置, 以便下次切換回當前線程時,能夠繼續執行字節碼指令, 所以每個線程都需要有自己的程序計數器。

      如果當前線程執行的是java方法,那么程序計數器記錄的是字節碼指令的地址。

      如果當前線程執行的native方法,那么程序計數器記錄的值為空(undefined)。

      程序計數器這部分內存區域是JVM中唯一不會出現OOM錯誤的區域

      程序計數器的生命周期與線程相同,即程序計數器隨著線程創建而創建, 隨著線程的銷毀而銷毀。

      使用 javap -c 反編譯class文件后的代碼如下, 紅框里的就是字節碼的偏移地址:

      Java虛擬機棧與程序計數器一樣,都是線程私有的部分,生命周期也跟線程一樣。

      Java虛擬機棧描述的是Java方法運行時的內存模型,它由一個一個的棧幀組成。

      棧幀是用于支持Java方法運行時的數據結構。 棧幀包含了局部變量表,操作數棧,動態連接,方法出口等信息。 每個方法執行時,都會在java虛擬機棧中創建一個棧幀。 對方法的調用和返回,就對應著棧幀的入棧和出棧的過程。

      Java虛擬機棧:

      局部變量表用于存儲方法參數和方法內定義的局部變量。 局部變量表存放了各種已知的數據類型的變量。?一個局部變量的類型可以是基本數據類型 (int,short,float,double,boolean,long,byte,char)或引用類型(reference)。 在Java代碼被編譯成class字節碼后,方法Code屬性的locals就確定了方法的局部變量表的大小。 局部變量表以slot為最小單位,一個slot代表4個字節,也就是32位長度的大小。

      操作數棧是一個后進先出(LIFO)的數據結構。?它存儲的是方法在進行數據運算時的元素。?和局部變量表一樣,操作數棧的每個元素的類型也可以是基本數據類型和引用類型。 操作數棧的深度不會超過 Code屬性的stack值。

      使用javap -c 反編譯class文件后可以得到的字節碼指令如下:

      了解動態連接首先需要了解符號引用和直接引用

      符號引用: 符號引用存于Class文件常量池。分為類的全限定名,方法名和描述符,字段名和描述符。

      直接引用: 指向目標的指針,可以簡單理解為目標的內存地址(如指向類的字段的內存地址)。

      Class文件常量池如下(javap -c 反編譯class文件后的字節碼):

      在虛擬機棧中,每個棧幀都包含了一個該棧幀所屬方法的符號引用, 持有這個符號引用的目的是為了支持方法調用過程中的動態連接。 這些符號引用有的一部分會在JVM類解析階段就會轉為直接引用,這部分轉換成為靜態解析。 還有一部分會在運行時轉為直接引用,這部分稱為動態連接。

      當方法執行時,有2種方式可以退出該方法。

      正常退出: 當方法執行時,執行到return指令,該方法就會正常退出。 一般來說,方法正常退出時,調用線程的程序計數器的值可以作為方法返回的地址, 棧幀中可能會保存這個計數器的值。

      異常退出: 在方法執行過程中遇到了異常,并且方法內部沒有處理這個異常,就會導致方法退出。 方法異常退出時,返回地址需要通過異常處理器表來確定的,棧幀中不會保存這部分值。

      無論何種退出方式,在方法退出后,都需要回到方法被調用的位置,程序才能繼續執行。

      本地方法棧與虛擬機棧的作用是相似的, 不過虛擬機棧是為執行Java方法提供服務的, 本地方法棧視為執行native方法提供服務的。?在本地方法執行的時候,也會在本地方法棧中創建棧幀, 用于存放該本地方法的局部變量表,操作數棧,動態連接和方法返回地址等信息。

      堆是JVM中內存占用最大的一塊區域,它是所有線程共享的一塊區域。 堆的作用是為對象分配內存并存儲和回收它們。 堆是垃圾回收的主要區域,所以堆區也被成為GC堆。

      堆區可以劃分為?新生代(Young Generation),老年代(Old Generation)?和 永久代(Permanent Generation),但永久代已被元空間代替,?元空間存儲的是類的元信息,幾乎不可能發生GC。

      新生代再細分可以分為:?Eden空間,From Survivor空間和To Survivor空間。

      缺省狀態下新生代占堆區的 1/3,老年代占堆區的2/3, eden空間占新生代的80%,2個Survivor空間棧新生代的20%, FromSurvivor和ToSurvivor的空間占比為1:1。

      (通過-XX:NewRatio參數可以調整新生代和老年代的空間占比) (通過-XX:SurvivorRatio參數可以調整eden和survivor的空間占比)

      發生在新生代的GC叫做Young GC或Minor GC, 發生在老年代的GC叫做Old GC或Major GC

      堆:

      PS: FromSurvivor和ToSurvivor這兩塊內存空間并不是固定的, 在進行GC的時候,這兩塊內存會輪流替換使用。這部分內容參考GC部分。

      PS: 有的文章說 Full GC與Major GC一樣是屬于對老年代的GC, 也有的文章說 Full GC 是對整個堆區的GC,所以這點需要各位同學自行分辨Full GC語義。 見:?知乎討論

      方法區在JVM規范里也是各個線程共享的一部分區域, 它用于存儲已被jvm加載的類的元信息,運行時常量池等數據。

      HotSpot虛擬機對于方法區的實現在jdk8之前為永久代,在jdk8之后, HotSpot移除了永久代,新增了元空間。

      元空間使用的是本地內存,所以元空間僅受本地物理內存的限制。 元空間存儲著已被加載的類的方法描述,字段描述,運行時常量池等信息。

      字符串常量池在jdk7已經從永久代轉移到了堆內存之中。

      無論是永久代還是元空間,都有可能發生OOM。

      JavaVirtualMachineError

      當前線程執行或請求的棧的大小超過了Java 虛擬機棧的最大空間(比如遞歸嵌套調用太深),就可能出現StackOverflowError錯誤

      發生OOM的情況:

      java heap space

      當需要為對象分配內存時,堆空間占用已經達到最大值, 無法繼續為對象分配內存,可能會出現OOM: java heap space錯誤。

      Requested array size exceeds VM limit

      當為數組分配內存時,數組需要的容量超過了虛擬機的限制范圍, 就會拋出OOM: Requested array size exceeds VM limit。

      根據我的測試(jdk11/jdk14),Integer.MAX_VALUE - 2 是虛擬機能為數組分配的最大容量,當數組長度為 Integer.MAX_VALUE - 1 時就會拋出 OOM:Requested array size exceeds VM limit

      GC overhead limit exceed

      垃圾回收器花費了很長時間GC,但是GC回收的內存非常少, 就可能拋出OOM:GC overhead limit exceed 錯誤。

      但是這點在我的機器上測試不出來,可能與jdk版本或gc收集器或Xmx分配內存的大小有關, 一直拋出的是java heap space

      Direct buffer memory

      當程序分配了超額的本地物理內存(native memory/ direct buffer), minor gc(young gc)并不會回收這部分內存, 只有 full gc才會回收直接內存,如果不發生full gc, 但直接內存卻被使用完了,那么可能會發生 OOM: Direct buffer memory。

      unable to create new native thread

      操作系統的線程資源是有限的, 如果程序創建的線程資源太多(無需超過平臺限制的線程資源上限), 就可能發生 OOM: unable to create new native thread 錯誤。

      Metaspace

      當加載到元空間中的類的信息太多,就有可能導致 OOM : Metaspace。

      使用cglib的庫,可以動態生成class,所以可以使用cglib測試此錯誤。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:智能制造生產管理就業前景(智能制造專業就業前景及薪酬)
      下一篇:中心值在excel中如何算
      相關文章
      中文字幕不卡亚洲| 亚洲国产中文字幕在线观看| 亚洲综合伊人久久大杳蕉| 亚洲性无码一区二区三区| 亚洲国产品综合人成综合网站| 亚洲AV无码一区二区三区DV| 亚洲热线99精品视频| 亚洲国产午夜中文字幕精品黄网站 | 亚洲中文久久精品无码| 亚洲综合精品网站在线观看| 亚洲AV无码一区二三区| 无码欧精品亚洲日韩一区夜夜嗨| 国产精品亚洲精品日韩动图| 国产精品亚洲一区二区三区在线观看 | 亚洲另类无码专区首页| 波多野结衣亚洲一级| 精品亚洲成在人线AV无码| ASS亚洲熟妇毛茸茸PICS| 亚洲乱码无人区卡1卡2卡3| 亚洲av乱码一区二区三区按摩 | 亚洲欧美日韩中文字幕一区二区三区 | 亚洲国产精品嫩草影院| 亚洲国产av玩弄放荡人妇| mm1313亚洲国产精品无码试看| 爱情岛亚洲论坛在线观看 | 亚洲国产成人久久一区久久| 亚洲天堂在线视频| 亚洲午夜久久久久久久久电影网| 亚洲国产婷婷六月丁香| 久久亚洲精品国产精品黑人| 久久久无码精品亚洲日韩蜜臀浪潮 | 亚洲日韩人妻第一页| 亚洲乳大丰满中文字幕| 亚洲AV日韩AV永久无码久久| 亚洲黄网在线观看| 在线a亚洲老鸭窝天堂av高清| 亚洲欧美在线x视频| 久久影视国产亚洲| 久久久久亚洲精品影视| 亚洲成a人片7777| 亚洲国产成人无码AV在线影院|