堆體系結構
一個JVM例項只存在一個堆記憶體,堆記憶體的大小是可調節的。類載入器讀取類檔案後,需要把類、方法、常量、變數放在堆記憶體中,儲存所有引用型別的真實資訊,以方便執行器指向,堆記憶體分為三個部分:年輕代、老年代、永久代。
Java7之前,堆記憶體在邏輯上分為:年輕代、老年代、永久代。物理上分為:年輕代、老年代
Java8:永久代 ---> 元空間
新生區是類的誕生、成長、消亡的區域。一個類在新生區產生,最後被垃圾回收器收集。新生區分為伊甸區和倖存者區。倖存者區分為倖存0區,倖存1區。
當伊甸區空間用完的時候,程式還需要建立物件,JVM的垃圾回收器將對伊甸區進行垃圾回收(Minor GC),將伊甸區中不再被其他物件引用的物件進行銷燬,將剩餘的物件移動到倖存0區。
若倖存0區(from區)滿了,對倖存0區進行垃圾回收,將剩餘的物件移動到倖存1區。如果倖存1區(to區)滿了,再移動到養老區。
如果養老區滿了,就產生了Major GC(Full GC),進行養老區的記憶體清理。如果執行了Full GC後依然無法進行物件的儲存,就會產生OOM異常,OutOfMemoryError。
異常:java.lang.OutOfMemoryError: Java heap space
JVM堆記憶體不夠,原因:
- JVM的堆記憶體設定的太小,可以調整-Xms、-Xmx
- 程式碼中建立了大量的大物件,並且長時間不能被垃圾回收器收集(存在被引用)
Minor GC的過程
Java堆從GC的角度可以細分為新生代(Eden區、from 存活區、to 存活區,空間比例8:1:1)和老年代(空間比例1:2)。
複製 ☞ 清空 ☞ 互換
1. eden、survivor from 複製到 survivor to,物件年齡+1。
當eden區滿,觸發第一次GC,存活物件拷貝到survivor from區。當eden區再次觸發GC,會掃描eden和from,對這兩個區進行垃圾回收,將存活的物件,複製到to區,物件年齡+1。(如果有物件年齡達到了老年的標準,拷貝到老年代,物件年齡+1)
2. 清空eden、survivor from
清空eden和survivor from中物件,此時from為。
3. survivor from 和 survivor to 互換
to區存在物件,變成下一次GC的from區,from區成為下一次GC的to區,部分物件會在form和to區域複製往來15次(JVM的MaxTenuringshold引數預設是15),如果最終還是存活,就存入老年代。
方法區和永久代
參考自部落格:https://www.jianshu.com/p/66e4e64ff278
在JDK1.6及之前,執行時常量池是方法區的一個部分,同時方法區裡面儲存了類的後設資料資訊、靜態變數、即時編譯器編譯後的程式碼(比如spring 使用IOC或者AOP建立bean時,或者使用cglib,反射的形式動態生成class資訊等)等。在JDK1.7及以後,JVM已經將執行時常量池從方法區中移了出來,在JVM堆開闢了一塊區域存放常量池。
方法區和堆都是各個執行緒共享的記憶體區域,方法區用於儲存虛擬機器載入的類資訊、普通常量、靜態常量、編譯器編譯後的程式碼等,雖然JVM規範將方法區描述為堆的一個邏輯部分,但它還有一個別名叫Non-Heap,目的是和堆分開。
方法區常被成為永久代,嚴格來說二者不同,只是用永久代來實現方法區而已,方法區和永久代的關係很像Java中介面和類的關係,類實現了介面,而永久代就是HotSpot虛擬機器對虛擬機器規範中方法區的一種實現方式。
永久代在JDK1.7之前有,是一個常駐記憶體區域,用於存放JDK自身攜帶的class、interface的後設資料,也就是說它儲存的是執行環境必須的類資訊,被裝在進此區域的資料是不會被垃圾回收器回收掉的,關閉jvm才會釋放這個區域所佔的記憶體。
HotSpot虛擬機器中存在三種垃圾回收現象,minor GC、major GC和full GC。對新生代進行垃圾回收叫做minor GC,對老年代進行垃圾回收叫做major GC,同時對新生代、老年代和永久代進行垃圾回收叫做full GC。許多major GC是由minor GC觸發的,所以很難將這兩種垃圾回收區分開。major GC和full GC通常是等價的,收集整個GC堆。但因為HotSpot VM發展了這麼多年,外界對各種名詞的解讀已經完全混亂了,當有人說“major GC”的時候一定要問清楚他想要指的是上面的full GC還是major GC。
元空間
參考自部落格:https://www.jianshu.com/p/66e4e64ff278
堆記憶體調優
在JDK1.7中
在JDK1.8中,元空間取代永久代。元空間和永久代的最大的區別是永久代使用的是JVM的堆記憶體,元空間不在虛擬機器中,而是使用本機實體記憶體。預設清空下,元空間只受本地記憶體限制,類的後設資料放入本地記憶體,字串常量池和型別靜態變數放入java堆,類的後設資料的載入量不再受MaxPermSize控制,而是由系統實際的可用空間來控制。
-Xms:初始分配大小,預設為實體記憶體的1/64
-Xmx:最大分配記憶體,預設為實體記憶體的1/4
-XX:+PrintGCDetails:輸出詳細的GC處理日誌
配置完Xms、Xmx後的輸出結果
java.lang.OutOfMemoryError: Java heap space異常GC處理日誌:
[GC (Allocation Failure) [PSYoungGen: 2045K->488K(2560K)] 2045K->781K(9728K), 0.0014360 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2534K->488K(2560K)] 2827K->1548K(9728K), 0.0008101 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2171K->504K(2560K)] 4318K->3194K(9728K), 0.0006870 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 2207K->0K(2560K)] [ParOldGen: 7037K->2826K(7168K)] 9245K->2826K(9728K), [Metaspace: 3454K->3454K(1056768K)], 0.0051352 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 5000K->5000K(9728K), 0.0003304 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 5000K->5000K(9728K), 0.0002962 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 5000K->3913K(7168K)] 5000K->3913K(9728K), [Metaspace: 3455K->3455K(1056768K)], 0.0028924 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 3913K->3913K(8704K), 0.0005099 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 3913K->3889K(7168K)] 3913K->3889K(8704K), [Metaspace: 3455K->3455K(1056768K)], 0.0072665 secs] [Times: user=0.06 sys=0.02, real=0.01 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674) at java.lang.StringBuilder.append(StringBuilder.java:208) at day05JVM01.T2.main(T2.java:15)
YoungGC
[GC (Allocation Failure) 記憶體分配失敗
[PSYoungGen: 2045K->488K(2560K)] 2045K->781K(9728K), 0.0014360 secs]
[GC型別:GC前young區的記憶體佔用->GC後young區的記憶體佔用(新生代的總記憶體)] GC前JVM堆記憶體佔用->GC後JVM堆記憶體佔用(JVM堆的總記憶體),GC耗時
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC使用者耗時,系統耗時,實際耗時]
FullGC
[Full GC (Allocation Failure)
[PSYoungGen: 0K->0K(1536K)]
[ParOldGen: 3913K->3889K(7168K)] 3913K->3889K(8704K),
[Metaspace: 3455K->3455K(1056768K)], 0.0072665 secs]
[Times: user=0.06 sys=0.02, real=0.01 secs]
什麼是GC?
GC是分類收集演算法,JVM在進行GC的時候並不是每次對三個區域一起回收,大部分時候是回收新生代。頻繁收集Young區,較少收集Old區,基本不動元空間。GC按照回收的區域分成了:普通GC minor GC和全域性GC Full GC
Minor GC:只針對新生代區域的GC,發生在新生代的垃圾收集,因為大多數JAVA物件存活率都不高,所以Minor GC的操作非常頻繁,垃圾回收的速度比較快。
Full GC:指發生在老年代的垃圾收集操作,出現Full GC,經常會伴隨至少一次的Minor GC(但不絕對)。Full GC的速度一般比Minor GC 慢10倍以上。
GC有四大演算法:引用計數法、複製演算法、標記清除、標記壓縮。