JVM8内存模型
文章目錄
- JVM內(nèi)存模型
- 1. 本地方法棧
- 2. 程序計數(shù)器(Program Counter Register)
- 3. 線程棧(Stack)
- 4. 方法區(qū)(Method Area)
- 5. 元空間(Metaspace)
- 6. 堆
JVM內(nèi)存模型
JVM內(nèi)存模型分為堆(heap)、元空間、棧、本地方法棧、程序計數(shù)器。
JDK8的內(nèi)存模型如下圖:
? ? ? ?堆和元空間是線程共享的,在Java虛擬機中只有一個堆、一個元空間,并在JVM啟動的時候就創(chuàng)建,JVM停止才銷毀。
? ? ? ?棧、本地方法棧、程序計數(shù)器是每個線程私有的,隨著線程的創(chuàng)建而創(chuàng)建,隨著線程的結束而死亡。
1. 本地方法棧
提供虛擬機使用到的本地Native方法服務。
2. 程序計數(shù)器(Program Counter Register)
? ? ? ?每個線程在創(chuàng)建后,都會產(chǎn)生自己的程序計數(shù)器和棧。程序計數(shù)器是一塊較小的內(nèi)存空間。由于CPU時間片輪限制,眾多線程在并發(fā)執(zhí)行過程中,處理器只會執(zhí)行某個線程中的一條指令,這樣必然涉及線程的切換。
? ? ? ?程序計數(shù)器用來存放下一個執(zhí)行指令的行號。線程恢復要依賴程序計數(shù)器。此區(qū)域不會發(fā)生內(nèi)存溢出異常。
3. 線程棧(Stack)
? ? ? ?JVM中的線程棧是描述Java方法執(zhí)行的內(nèi)存區(qū)域,它是線程私有的。每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
棧幀存儲著局部變量、操作數(shù)棧、動態(tài)鏈接、方法出口等信息,局部變量存儲方法內(nèi)的局部變量,操作數(shù)棧用來計算時臨時存放變量,動態(tài)鏈接存放方法的元信息等,方法出口記錄如哪個方法的本方法等。通過javap -c XX.class查看字節(jié)碼文件。
? ? ? ?如果線程請求的棧深度大于虛擬機所允許的深度,將會拋出stackoverflowError通常出現(xiàn)在遞歸方法中;
? ? ? ?如果虛擬機可以動態(tài)擴展,但是無法申請到足夠的內(nèi)存時,就會拋出outOfMemoryError異常。
4. 方法區(qū)(Method Area)
? ? ? ?方法區(qū)中存放已經(jīng)被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。是JDK7之前存在的一個概念,這里僅做簡單回顧,模型圖如下:
? ? ? ?方法區(qū)與堆(Java Heap)一樣,是各個線程共享的內(nèi)存區(qū)域。
-
運行時常量池
? ? ? ?運行時常量池是方法區(qū)的一部分,存放著class文件元信息描述,編譯后的代碼數(shù)據(jù),引用類型數(shù)據(jù),類文件常量池。
? ? ? ?JDK1.7之前運行時常量池是方法區(qū)的一部分,JDK1.7及之后版本已經(jīng)將運行時常量池從方法區(qū)中移了出來,開辟了一塊區(qū)域Metaspace(元空間)存放運行時常量池,注意字符串常量池移至堆中。 -
PermGen(永久代)
? ? ? ?絕大部分 Java 程序員應該都見過 “java.lang.OutOfMemoryError: PermGen space “這個異常。這里的 “PermGen space”其實指的就是方法區(qū)。不過方法區(qū)和“PermGen space”又有著本質(zhì)的區(qū)別,前者是 JVM 內(nèi)存回收的規(guī)范,而后者則是 JVM 規(guī)范的一種實現(xiàn),使用永久代來實現(xiàn)方法區(qū)而已。這樣HotSpot的垃圾收集器就能像管理Java堆一樣管理這部分內(nèi)存。簡單點說就是HotSpot虛擬機中內(nèi)存模型的分代,其中新生代和老生代在堆中,永久代使用方法區(qū)實現(xiàn)。
? ? ? ?類及方法的信息等比較難確定其大小,因此對于永久代的大小指定比較困難,太小容易出現(xiàn)永久代溢出,太大則容易導致老年代溢出。由于方法區(qū)主要存儲類的相關信息,所以對于動態(tài)生成類的情況比較容易出現(xiàn)永久代的內(nèi)存溢出,容易產(chǎn)生Perm區(qū)的OOM。比如某個實際Web工程中,因為功能點比較多,在運行過程中,要不斷動態(tài)加載很多的類,經(jīng)常出現(xiàn)致命錯誤:Exception in thread ‘dubbo client x.x connector' java.lang.OutOfMemoryError: PermGenspac,為解決該問題,需要設定運行參數(shù)-XX:MaxPermSize= l280m,如果部署到新機器上,往往會因為JVM參數(shù)沒有修改導致故障再現(xiàn)。不熟悉此應用的人排查問題時往往苦不堪言。除此之外,永久代會為 GC 帶來不必要的復雜度,并且回收效率偏低;字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出。
5. 元空間(Metaspace)
? ? ? ?JDK8使用元空間替換永久代,元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。之前永久代的內(nèi)容:類元信息、字段、靜態(tài)屬性、方法、常量,還有運行時常量池等都移動至元空間,但是字符串常量移至堆內(nèi)存。
? ? ? ?元空間在本地內(nèi)存中分配,它并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域,它直接從操作系統(tǒng)中分配,因此不受Java堆大小的限制,但是會受到本機總內(nèi)存的大小及處理器尋址空間的限制,因此它也可能導致OutOfMemoryError異常出現(xiàn)。“元空間”的大小可以動態(tài)調(diào)整,通過以下參數(shù)來指定元空間大小:
-XX:MetaspaceSize,初始空間大小,達到該值就會觸發(fā)垃圾收集進行類型卸載,同時GC會對該值進行調(diào)整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值
-XX:MaxMetaspaceSize,最大空間,默認是沒有限制的
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導致的垃圾收集
元空間詳解
6. 堆
? ? ? ?Heap存儲著幾乎所有的對象及數(shù)組,JVM8中把靜態(tài)變量(字符串常量池)也移到堆區(qū)進行存儲。
? ? ? ?堆是OOM故障最主要的發(fā)源地,也是是垃圾回收的主要區(qū)域,所以也被稱為GC堆。通常情況下,它占用的空間是所有內(nèi)存區(qū)域中最大的,但如果無節(jié)制地創(chuàng)建大量對象,也容易消耗完所有的空間。堆的內(nèi)存空間既可以固定大小,也可運行時動態(tài)地調(diào)整,通過如下參數(shù)設定初始值和最大值,比如-Xms256M. -Xmx1024M。其中-X表示它是JVM運行參數(shù),ms是memorystart初始堆容量的簡稱 ,mx是memory max最大堆容量的簡稱。但是在通常情況下,服務器在運行過程中,堆空間不斷地擴容與回縮,勢必形成不必要的系統(tǒng)壓力,所以在線上生產(chǎn)環(huán)境中,JVM的Xms和Xmx設置成一樣大小,避免在GC后調(diào)整堆大小時帶來的額外壓力。
? ? ? ?堆分成兩大塊:新生代和老年代,對象產(chǎn)生之初在新生代,步入暮年時進入老年代。
? ? ? ?新生代又分為1個Eden區(qū)+ 2個Survivor區(qū),8:1:1的比例。
? ? ? ?絕大部分對象在Eden(意為伊甸園)區(qū)生成,當Eden區(qū)裝填滿的時候,會觸發(fā)Young GC。垃圾回收的時候,在Eden區(qū)實現(xiàn)清除策略,沒有被引用的對象則直接回收。依然存活的對象會被移送到Survivor(幸存者)區(qū),這個區(qū)真是名副其實的存在。Survivor 區(qū)分為S0和S1兩塊內(nèi)存空間,送到哪塊空間呢?每次Young GC的時候,將存活的對象復制到未使用的那塊空間,然后將當前正在使用的空間完全清除,交換兩塊空間的使用狀態(tài)。
? ? ? ?如果Young GC要移送的對象大于Survivor區(qū)容量上限,或者超大對象的閾值超過eden分配擔保設置值的上限,則直接移交給老年代.如果老年代也無法放下,則會觸發(fā)Full Garbage Collection(Full GC),如果依然無法放下,則拋OOM.。
? ? ? ?假如一些沒有進取心的對象以為可以一直在新生代的Survivor區(qū)交換來交換去,那就錯了。每個對象都有一個計數(shù)器,每次Young GC都會加1。-XX:MaxTenuringThreshold參數(shù)能配置計數(shù)器的值到達某個閾值的時候,對象從新生代晉升至老年代。默認值是15,可以在Survivor 區(qū)交換14次之后,晉升至老年代。
? ? ? ?堆出現(xiàn)OOM的概率是所有內(nèi)存耗盡異常中最高的。出錯時的堆內(nèi)信息對解決問題非常有幫助,所以給JVM設置運行參數(shù)-XX:+HeapDumpOnOutOfMemoryError,讓JVM遇到OOM異常時能輸出堆內(nèi)信息,使用-XX:HeapDumpPath參數(shù)指定dump路徑。利用JVM參數(shù)-XX:OnOutOfMemoryError可以在發(fā)生OOM異常時,運行一個本機的腳本或指令。
jVM內(nèi)存模型
內(nèi)存回收
總結
- 上一篇: String字符串相等判断
- 下一篇: 开发中常用工具