<ul id="seequ"><sup id="seequ"></sup></ul><tfoot id="seequ"></tfoot>
  • <tfoot id="seequ"><delect id="seequ"></delect></tfoot>
  • <ul id="seequ"></ul>
  • <small id="seequ"></small>

    JVM - 垃圾回收篇筆記

    2023-02-22 20:01:08 來源:騰訊云

    垃圾回收

    如何判斷對象已死

    引用計數法

    早期Python虛擬機采用

    當對象被引用時,引用計數器++,取消引用時--


    (資料圖片僅供參考)

    無法解決問題:相互循環引用

    可達性分析算法

    通過一系列成為GC Roots的根對象作為起始節點集,從GCRoots開始向下搜索,搜索的路徑為”引用鏈“,如果沒有引用鏈,也就是不可達,說明對象不可能再被使用

    在Java技術體系中,固定可作為GCRoots的對象包括:

    在JVMStack中引用的對象在方法區中類靜態屬性引用的變量在方法區中常量引用的對象在本地方法棧中JNI引用的對象JVM內部引用,基本數據類型對應的Class對象、常駐異常對象、系統類加載器所有被同步鎖所持有的對象反應JVM內部情況的JMXBean、JVMTI中注冊的回調、本地代碼緩存等根據所選擇的垃圾收集器、當前回收區域,采用其他對象臨時性的加入

    MAT

    jmap -dump:format=b,live,file=11.bin

    引用

    強、軟(SoftReference)、弱(WeakReference)

    軟引用:

    必須配合引用隊列:虛(PPhantomRefernce)、終結(FinalReference)

    終結:使用finallize方法

    引用隊列關聯

    回收對象

    利用可達性算法,不可達進行第一次標記。

    標記后進行篩選:是否有必要執行finalize()方法,如果沒有重寫或者已經被調用過,那么就視為沒有必要執行

    如果有必要執行,那么會放入一個F-Queued的隊列(引用隊列)中,并在稍后由一條由虛擬機自動建立的,低調度優先級的Finalizer線程去執行他們的finalize()方法。

    為了防止出現阻塞,F-Queue不會等待方法執行結束,否則會讓其他對象永久處于等待,造成內存回收子系統 崩潰。

    如果finalize()成功執行,重新于引用鏈上任一對象建立關系即可:比如把this賦值給某個類變量或者對象的成員變量,那么在第二次標記時,他將會被移出”即將回收“的集合。如果此時對象仍然沒有逃脫,那么就要被回收了。

    Systen.gc()還是系統自動回收,都只會調用一次finalize()方法

    回收方法區

    主要回收兩個內容:廢棄常量不再使用類型

    判斷一個常量是否廢棄相對簡單:該常量曾經進入常量池中,但現在又沒有一個對象的值和該常量相同,也就是說沒有對象引用該常量,且虛擬機中也沒有其他地方引用該字面量,則該常量可以被垃圾回收,清理出常量池。

    類、方法、字段的符號引用也類似。

    但是判斷一個類是否廢棄需要滿足下面三個條件:

    該類的所有實例都已被回收(包括派生類)加載該類的類加載器已經被回收,這個條件除非是經過精心設計的可替換類加載器的場景,如OSGi、JSP的重加載等,否則通常很難達成該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過u哦反射訪問該類的方法。

    對于是否被零星回收,HotSpot提供了 -Xnoclassgc 參數進行控制,還可以使用 -XX:+TraceClassLoading、-XX:TraceClassUnLoading查看類加載和寫在信息(前兩個可以在Product版JVM中使用,最后一種需要FastDebug版的JVM支持)。

    垃圾回收算法

    標記清除

    優點:記錄垃圾碎片的起止地址,快速,不用清零缺點:碎片較多

    標記整理

    優點:沒有內存碎片缺點:速度慢

    復制算法

    優點:不會有內存碎片需要占用雙倍內存空間

    分代回收

    根據對象生命周期的不同特點分為新生代和老年代

    新生代:處理不常用的對象

    老年代:處理常用的對象

    在新生代中,每次垃圾收集時都發現有大批對象死去,而每次回收后存活的少量對象將會逐步晉升到老年代釋放

    新生代垃圾回收

    第一次垃圾回收

    新創建的對象選擇伊甸園空間

    當伊甸園空間滿了以后會進行垃圾護手,新生代的垃圾回收叫做Minor GC,回收后將存活對象賦值到幸存區,并將壽命+1

    再將to和form交換指向位置

    第二次垃圾回收

    在第一次的回收基礎上,對幸存區的某一個進行取消引用

    此時to中的1已經被取消引用,所以直接刪除

    最后from和to交換位置、放入新對象

    當年齡達到一定闕值后放入老年代

    老年代垃圾回收

    當新生代內存不夠時,會嘗試促發FullGC進行老年代的垃圾回收,準確來說是整個堆空間

    總結

    對象首先非陪在伊甸園區域新生代空間不足時,出發MinorGC,伊甸園和from存活的對象使用copy復制到to中,存活的對象年齡+1并且交換fromtoMinor GC 會引發 stop the world (暫停 其他用戶正在執行的線程 wait,等垃圾回收結束后,用戶線程才恢復運行 notifyAll) 當對象壽命超過闕值時,會晉升至老年代,最大壽命是15(4bit,但是不同的垃圾回收器不同)當老年代空間不足,會先嘗試促發MinorGC,如果空間仍不足,那么觸發Full GC,STW暫停時間(老年代回收時間)更長

    GC相關參數

    GC分析

    -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc

    代碼:

    package org.example;import java.io.IOException;public class Main {    public static int a=5;    public static void main(String[] args) throws InterruptedException {        Thread.sleep(10000000000l);        return ;    }}

    E:F:T=8:1:1,其中T默認是已使用內存

    既然剛啟動,那為什么EdenSpace會有占用呢?

    類加載過程

    思考:當new的對象占用內存如果超過了幸存區內存會發生什么?內存溢出?動態占比分配?試驗代碼:package org.example; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; public class Main { public static int a=5; public static void main(String[] args) throws InterruptedException, IOException { System.in.read(); ArrayList bytes=new ArrayList<>(); System.out.println("添加對象中"); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); return ; } }結果:D:\Program\jdk\bin\java.exe -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc "-javaagent:D:\Program\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=61656:D:\Program\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath H:\JVM\gc\target\classes org.example.Main 0.003sgc -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead. 0.010sgc Using Serial 0.010sgc,init Version: 17.0.1+12-39 (release) 0.010sgc,init CPUs: 8 total, 8 available 0.010sgc,init Memory: 13810M 0.010sgc,init Large Page Support: Disabled 0.010sgc,init NUMA Support: Disabled 0.010sgc,init Compressed Oops: Enabled (32-bit) 0.010sgc,init Heap Min Capacity: 20M 0.010sgc,init Heap Initial Capacity: 20M 0.010sgc,init Heap Max Capacity: 20M 0.010sgc,init Pre-touch: Disabled 0.010sgc,metaspace CDS archive(s) mapped at: [0x0000000800000000-0x0000000800bc0000-0x0000000800bc0000), size 12320768, SharedBaseAddress: 0x0000000800000000, ArchiveRelocationMode: 0. 0.011sgc,metaspace Compressed class space mapped at: 0x0000000800c00000-0x0000000840c00000, reserved size: 1073741824 0.011sgc,metaspace Narrow klass base: 0x0000000800000000, Narrow klass shift: 0, Narrow klass range: 0x100000000 10.160sgc,start GC(0) Pause Young (Allocation Failure) 10.165sgc,heap GC(0) DefNew: 8192K(9216K)->1023K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->1023K(1024K) 10.165sgc,heap GC(0) Tenured: 0K(10240K)->1443K(10240K) 10.165sgc,metaspace GC(0) Metaspace: 2679K(2880K)->2679K(2880K) NonClass: 2400K(2496K)->2400K(2496K) Class: 279K(384K)->279K(384K) 10.165sgc GC(0) Pause Young (Allocation Failure) 8M->2M(19M) 4.780ms 10.165sgc,cpu GC(0) User=0.00s Sys=0.02s Real=0.01s 10.384sgc,start GC(1) Pause Young (Allocation Failure) 10.390sgc,heap GC(1) DefNew: 9215K(9216K)->482K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 1023K(1024K)->482K(1024K) 10.390sgc,heap GC(1) Tenured: 1443K(10240K)->2463K(10240K) 10.390sgc,metaspace GC(1) Metaspace: 4881K(5120K)->4881K(5120K) NonClass: 4357K(4480K)->4357K(4480K) Class: 523K(640K)->523K(640K) 10.390sgc GC(1) Pause Young (Allocation Failure) 10M->2M(19M) 5.666ms 10.390sgc,cpu GC(1) User=0.00s Sys=0.00s Real=0.01s 26.252sgc,start GC(2) Pause Full (System.gc()) 26.252sgc,phases,start GC(2) Phase 1: Mark live objects 26.257sgc,phases GC(2) Phase 1: Mark live objects 5.380ms 26.257sgc,phases,start GC(2) Phase 2: Compute new object addresses 26.259sgc,phases GC(2) Phase 2: Compute new object addresses 2.149ms 26.259sgc,phases,start GC(2) Phase 3: Adjust pointers 26.262sgc,phases GC(2) Phase 3: Adjust pointers 3.118ms 26.263sgc,phases,start GC(2) Phase 4: Move objects 26.263sgc,phases GC(2) Phase 4: Move objects 0.855ms 26.264sgc,heap GC(2) DefNew: 8083K(9216K)->0K(9216K) Eden: 7600K(8192K)->0K(8192K) From: 482K(1024K)->0K(1024K) 26.264sgc,heap GC(2) Tenured: 2463K(10240K)->3900K(10240K) 26.264sgc,metaspace GC(2) Metaspace: 7956K(8256K)->7956K(8256K) NonClass: 7083K(7232K)->7083K(7232K) Class: 872K(1024K)->872K(1024K) 26.264sgc GC(2) Pause Full (System.gc()) 10M->3M(19M) 12.256ms 26.264sgc,cpu GC(2) User=0.02s Sys=0.00s Real=0.01s 添加對象中 [B@3b07d329 49.675sgc,start GC(3) Pause Young (Allocation Failure) 49.678sgc,heap GC(3) DefNew: 8192K(9216K)->35K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->35K(1024K) 49.678sgc,heap GC(3) Tenured: 3900K(10240K)->9020K(10240K) 49.678sgc,metaspace GC(3) Metaspace: 8137K(8384K)->8137K(8384K) NonClass: 7263K(7360K)->7263K(7360K) Class: 874K(1024K)->874K(1024K) 49.678sgc GC(3) Pause Young (Allocation Failure) 11M->8M(19M) 2.957ms 49.678sgc,cpu GC(3) User=0.00s Sys=0.02s Real=0.00s 96.458sgc,start GC(4) Pause Young (Allocation Failure) 96.458sgc GC(4) Pause Young (Allocation Failure) 14M->14M(19M) 0.078ms 96.458sgc,cpu GC(4) User=0.00s Sys=0.00s Real=0.00s 96.458sgc,start GC(5) Pause Full (Allocation Failure) 96.458sgc,phases,start GC(5) Phase 1: Mark live objects 96.467sgc,phases GC(5) Phase 1: Mark live objects 8.235ms 96.467sgc,phases,start GC(5) Phase 2: Compute new object addresses 96.469sgc,phases GC(5) Phase 2: Compute new object addresses 2.466ms 96.469sgc,phases,start GC(5) Phase 3: Adjust pointers 96.473sgc,phases GC(5) Phase 3: Adjust pointers 4.087ms 96.473sgc,phases,start GC(5) Phase 4: Move objects 96.474sgc,phases GC(5) Phase 4: Move objects 0.946ms 96.475sgc,heap GC(5) DefNew: 5571K(9216K)->0K(9216K) Eden: 5535K(8192K)->0K(8192K) From: 35K(1024K)->0K(1024K) 96.475sgc,heap GC(5) Tenured: 9020K(10240K)->8999K(10240K) 96.475sgc,metaspace GC(5) Metaspace: 8213K(8448K)->8092K(8448K) NonClass: 7339K(7424K)->7244K(7424K) Class: 874K(1024K)->848K(1024K) 96.475sgc GC(5) Pause Full (Allocation Failure) 14M->8M(19M) 16.586ms 96.475sgc,cpu GC(5) User=0.02s Sys=0.00s Real=0.02s [B@3b07d329 [B@682a0b20 96.477sgc,start GC(6) Pause Young (Allocation Failure) 96.477sgc GC(6) Pause Young (Allocation Failure) 13M->13M(19M) 0.086ms 96.477sgc,cpu GC(6) User=0.00s Sys=0.00s Real=0.00s 96.477sgc,start GC(7) Pause Full (Allocation Failure) 96.477sgc,phases,start GC(7) Phase 1: Mark live objects 96.485sgc,phases GC(7) Phase 1: Mark live objects 8.022ms 96.485sgc,phases,start GC(7) Phase 2: Compute new object addresses 96.487sgc,phases GC(7) Phase 2: Compute new object addresses 1.451ms 96.487sgc,phases,start GC(7) Phase 3: Adjust pointers 96.491sgc,phases GC(7) Phase 3: Adjust pointers 4.088ms 96.491sgc,phases,start GC(7) Phase 4: Move objects 96.492sgc,phases GC(7) Phase 4: Move objects 0.856ms 96.492sgc,heap GC(7) DefNew: 5217K(9216K)->5121K(9216K) Eden: 5217K(8192K)->5121K(8192K) From: 0K(1024K)->0K(1024K) 96.492sgc,heap GC(7) Tenured: 8999K(10240K)->8962K(10240K) 96.492sgc,metaspace GC(7) Metaspace: 8097K(8448K)->8028K(8448K) NonClass: 7247K(7424K)->7189K(7424K) Class: 849K(1024K)->838K(1024K) 96.492sgc GC(7) Pause Full (Allocation Failure) 13M->13M(19M) 15.333ms 96.492sgc,cpu GC(7) User=0.02s Sys=0.00s Real=0.02s 96.492sgc,start GC(8) Pause Full (Allocation Failure) 96.492sgc,phases,start GC(8) Phase 1: Mark live objects 96.498sgc,phases GC(8) Phase 1: Mark live objects 6.004ms 96.498sgc,phases,start GC(8) Phase 2: Compute new object addresses 96.500sgc,phases GC(8) Phase 2: Compute new object addresses 1.182ms 96.500sgc,phases,start GC(8) Phase 3: Adjust pointers 96.504sgc,phases GC(8) Phase 3: Adjust pointers 4.837ms 96.505sgc,phases,start GC(8) Phase 4: Move objects 96.507sgc,phases GC(8) Phase 4: Move objects 2.185ms 96.507sgc,heap GC(8) DefNew: 5121K(9216K)->5121K(9216K) Eden: 5121K(8192K)->5121K(8192K) From: 0K(1024K)->0K(1024K) 96.507sgc,heap GC(8) Tenured: 8962K(10240K)->8450K(10240K) 96.507sgc,metaspace GC(8) Metaspace: 8028K(8448K)->8028K(8448K) NonClass: 7189K(7424K)->7189K(7424K) Class: 838K(1024K)->838K(1024K) 96.507sgc GC(8) Pause Full (Allocation Failure) 13M->13M(19M) 14.864ms 96.507sgc,cpu GC(8) User=0.02s Sys=0.00s Real=0.02s 96.509sgc,heap,exit Heap 96.509sgc,heap,exit def new generation total 9216K, used 5405K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) 96.509sgc,heap,exit eden space 8192K, 65% used [0x00000000fec00000, 0x00000000ff1475c8, 0x00000000ff400000) 96.509sgc,heap,exit from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) 96.509sgc,heap,exit to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) 96.509sgc,heap,exit tenured generation total 10240K, used 8450K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) 96.509sgc,heap,exit the space 10240K, 82% used [0x00000000ff600000, 0x00000000ffe40b90, 0x00000000ffe40c00, 0x0000000100000000) 96.509sgc,heap,exit Metaspace used 8034K, committed 8448K, reserved 1056768K 96.509sgc,heap,exit class space used 839K, committed 1024K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at org.example.Main.main(Main.java:21) 進程已結束,退出代碼1

    最開始放到新生代,但是當繼續new的時候,直接放入了老年代.

    重復實驗,發現如果幸存區空間充足,對象會放入幸存區并且延壽,否則直接將待延壽對象晉升到老年代,當老年代空間不足時,拋出oom異常.

    package org.example; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; public class Main { public static int a=5; public static void main(String[] args) throws InterruptedException, IOException { System.in.read(); ArrayList bytes=new ArrayList<>(); System.out.println("添加對象中"); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*8); } }添加對象中 [B@3b07d329 34.804sgc,start GC(4) Pause Young (Allocation Failure) 34.806sgc,heap GC(4) DefNew: 8192K(9216K)->14K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->14K(1024K) 34.806sgc,heap GC(4) Tenured: 3897K(10240K)->9017K(10240K) 34.806sgc,metaspace GC(4) Metaspace: 8072K(8320K)->8072K(8320K) NonClass: 7196K(7296K)->7196K(7296K) Class: 875K(1024K)->875K(1024K) 34.806sgc GC(4) Pause Young (Allocation Failure) 11M->8M(19M) 2.195ms 34.806sgc,cpu GC(4) User=0.00s Sys=0.00s Real=0.00s 59.066sgc,start GC(5) Pause Young (Allocation Failure) 59.066sgc GC(5) Pause Young (Allocation Failure) 11M->11M(19M) 0.075ms 59.066sgc,cpu GC(5) User=0.00s Sys=0.00s Real=0.00s 59.066sgc,start GC(6) Pause Full (Allocation Failure) 59.066sgc,phases,start GC(6) Phase 1: Mark live objects 59.074sgc,phases GC(6) Phase 1: Mark live objects 7.218ms 59.074sgc,phases,start GC(6) Phase 2: Compute new object addresses 59.076sgc,phases GC(6) Phase 2: Compute new object addresses 1.923ms 59.076sgc,phases,start GC(6) Phase 3: Adjust pointers 59.080sgc,phases GC(6) Phase 3: Adjust pointers 3.998ms 59.080sgc,phases,start GC(6) Phase 4: Move objects 59.080sgc,phases GC(6) Phase 4: Move objects 0.713ms 59.082sgc,heap GC(6) DefNew: 3000K(9216K)->0K(9216K) Eden: 2985K(8192K)->0K(8192K) From: 14K(1024K)->0K(1024K) 59.082sgc,heap GC(6) Tenured: 9017K(10240K)->9000K(10240K) 59.082sgc,metaspace GC(6) Metaspace: 8199K(8448K)->8078K(8448K) NonClass: 7323K(7424K)->7229K(7424K) Class: 875K(1024K)->849K(1024K) 59.082sgc GC(6) Pause Full (Allocation Failure) 11M->8M(19M) 15.316ms 59.082sgc,cpu GC(6) User=0.02s Sys=0.00s Real=0.01s 59.082sgc,start GC(7) Pause Full (Allocation Failure) 59.082sgc,phases,start GC(7) Phase 1: Mark live objects 59.090sgc,phases GC(7) Phase 1: Mark live objects 7.740ms 59.090sgc,phases,start GC(7) Phase 2: Compute new object addresses 59.091sgc,phases GC(7) Phase 2: Compute new object addresses 1.085ms 59.091sgc,phases,start GC(7) Phase 3: Adjust pointers 59.094sgc,phases GC(7) Phase 3: Adjust pointers 3.000ms 59.094sgc,phases,start GC(7) Phase 4: Move objects 59.095sgc,phases GC(7) Phase 4: Move objects 1.331ms 59.095sgc,heap GC(7) DefNew: 0K(9216K)->0K(9216K) Eden: 0K(8192K)->0K(8192K) From: 0K(1024K)->0K(1024K) 59.095sgc,heap GC(7) Tenured: 9000K(10240K)->8452K(10240K) 59.095sgc,metaspace GC(7) Metaspace: 8078K(8448K)->8009K(8448K) NonClass: 7229K(7424K)->7171K(7424K) Class: 849K(1024K)->838K(1024K) 59.095sgc GC(7) Pause Full (Allocation Failure) 8M->8M(19M) 13.684ms 59.096sgc,cpu GC(7) User=0.02s Sys=0.00s Real=0.01s 59.097sgc,heap,exit Heap 59.097sgc,heap,exit def new generation total 9216K, used 220K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) 59.097sgc,heap,exit eden space 8192K, 2% used [0x00000000fec00000, 0x00000000fec37390, 0x00000000ff400000) 59.097sgc,heap,exit from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) 59.097sgc,heap,exit to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) 59.097sgc,heap,exit tenured generation total 10240K, used 8452K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) 59.097sgc,heap,exit the space 10240K, 82% used [0x00000000ff600000, 0x00000000ffe41268, 0x00000000ffe41400, 0x0000000100000000) 59.097sgc,heap,exit Metaspace used 8013K, committed 8448K, reserved 1056768K 59.097sgc,heap,exit class space used 839K, committed 1024K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at org.example.Main.main(Main.java:17) 進程已結束,退出代碼1

    大對象_oom

    剛剛想到問題并實現了,下面就是這個類似的.?

    沒錯,這種情況如果老年代空間足夠(前提是新生代不夠用)直接晉升

    如果超過老年代的話,很明顯就是OOM,不過會提前做個自救工作,GC

    線程中OOM

    當某個線程發生OOM時,會影響主線程嗎?

    無論時哪個線程,只要是一個進程,那么堆內存是共享的但是當一個線程拋出異常后,JVM會釋放的該線程所占用的內存資源

    綜上,不會影響

    垃圾回收器

    串行 —— Serial

    單線程堆內存較小,適合個人電腦
    -XX:+uSEsERIALgc=Serial+SerialOld

    Serial:作用于新生代,標記復制

    SerialOld:作用于老年代,標記整理

    吞吐量優先

    吞吐量:運行用戶代碼時間/(運行用戶代碼時間+運行垃圾收集時間)

    多線程堆內存較大,多核cpu讓單位時間內,stw的時間最短
    #JDK8下默認開啟-XX:+UseParallelGC ~ -XX:+UseParallelOldGC#需要手動開啟#自適應新生代大小-XX:+UseAdaptiveSizePolicy#根據目標調整吞吐量目標,如果達不到則調整堆的大小 1/(1+ratio) => 垃圾回收時間/總的運行時間,結果表示每100分鐘執行n分鐘-XX:GCTimeRatio=ratio#GC暫停時間-XX:MaxGCPauseMillis=ms-XX:ParallelGCThreads=n

    默認線程數為cpu核數

    響應優先 —— CMS 并發標記清除收集器

    多線程堆內存較大,多核cpu盡可能讓單次stop onworld(stw)的時間的最短

    并行和并發

    這里本來是OS的內容,在這里再提一下

    并行
    并發(Concurrent)
    并行+并發

    響應優先

    -XX:+UseConcMarkSweepGc-XX:+UserParNewGC#老年代補救-XX:SerialOld#并行線程數,CPU核數-XX:ParallelGCThreads=n#并發線程數,一般設置為并行線程數的1/4-XX:ConGCThreads=threads-XX:CMSInitiatingOccupancyFraction=parent-XX:+CMSScavengeBeforeRemark
    初始標記:只標記與GCRoots能直接關聯到的對象并發標記:從GCRoots關聯對象開始遍歷整個圖重新標記:修復并發標記期間,用戶程序運作而導致標記產生變動的那部分對象的標記記錄清除階段:清楚標記階段判斷的已死亡對象

    缺點

    雖然不會導致用戶線程停頓,但是導致應用程序變慢,降低總吞吐量CMS默認啟動的回收線程數是$$(處理器核心數量+3)/4$$

    ,也就是說當核心數≥4時,并發回收垃圾手機線程只占用不超過25%的處理器運算資源,并且伴隨著核心數增多而下降

    當核心數<4時,對用戶線程的影響變大 擴展:增量式并發收集器無法處理 浮動垃圾 ,有可能出現CMF(并發mode失敗)從而導致另一次STW的Full GC產生

    HotSpot算法細節實現

    根節點枚舉

    GC Roots主要在全局性的引用和執行上下文中,且目前Java應用越做越龐大,類、常量很多,逐個檢查耗費時間較長。

    所有收集器根節點枚舉必須STW可達性分析算法耗時最長的查找引用鏈過程已經可以做到與用戶線程一起并發根節點枚舉必須在保證一致性的快照中才得以進行——枚舉期間,根節點對象的引用關系不發生變化

    目前主流的虛擬機均采用準確式垃圾收集(虛擬機可以知道內存中某個位置的數據具體是什么類型),當用戶線程停頓下來以后,并不需要一個不漏地檢查完所有執行上下文和全局的應用位置,虛擬機應當時又辦法直接得到哪些地方存放著用戶引用的。

    在HotSpot中,使用一組稱為OopMap的數據結構來解決準確式垃圾回收的問題,在類加載動作完成的時候,虛擬機會把對象內什么偏移量上是什么類型的數據計算出來,在即使編譯過程中,也會在特定位置記錄棧、寄存器中哪些位置是引用。

    安全點

    OopMap協助虛擬機快速準確完成GC Roots的枚舉,但是可能導致引用關系變化,換句話說

    導致OopMap內容發生變化的指令非常多,如果每個指令都生成對應的OopMap,那么會需要大量的額外存儲空間,這樣垃圾收集伴隨的空間成本將無比高昂

    實際上HotSpot的確沒有為每條指令生成OopMap,只是在特定的位置記錄了這些信息你,這些位置被稱為安全點

    安全點的設定,決定了用戶程序執行時并非在代碼指定流的任意位置都能停下來進行垃圾收集,必須到達安全點才能暫停。

    安全點的選定

    不能太少,也不能讓收集器的等待時間過長不能太頻繁增大運行時的內存負荷以”是否具有讓程序長時間執行的特征“為標準進行選定
    - 指令序列復用
    - 方法調用    - 循環跳轉    - 異常跳轉

    安全區域

    安全點機制只能保證程序執行時,在不太長的時間內就會遇到可進入垃圾手機的過程的安全點。

    不執行的時候呢?

    ——沒有分配處理器時間 Sleep or Blocked,此時線程無法響應虛擬機中斷請求,不能再走到安全的地方終端掛起自己,虛擬機也顯然不可能持續等待線程重新杯激活分配處理器時間。

    為了解決這一問題,引入了安全區域(Safe Region)

    安全區域是指能夠確保在某一段代碼片段中,引用關系不會發生變化,因此在這個區域中任意地方開始垃圾收集都是安全的。(背擴展拉伸了的安全點)

    當用戶線程執行刀安全區域里的代碼時,首先會標識自己已經進入了安全區域,虛擬機在進行垃圾收集時不必去管此類線程,當線程離開安全區域時,檢測虛擬機是否已經完成了根節點枚舉(或垃圾收集過程中其他需要暫停用戶線程的階段),如果完成,線程就當作沒事發生過繼續執行,否則一直等待,知道收到可以離開安全區域的信號為止

    記憶集與卡表

    分代收集理論提到了為解決 對象跨代引用所帶來的問題,垃圾收集器在新生代中建立了名為記憶集的數據結構-

    記憶集

    用于避免把整個老年代加進GCRoots掃描范圍事實上不只是新生代、老年代之間才可有跨代引用的問題,所有設計部分區域收集(Partial GC)行為的垃圾收集器(G1,ZGC,Shenandoah)都會面臨相同的問題記憶集是一種用于記錄從非收集區域指向收集區域的指針集合的抽象數據結構。
    - 如果不考慮成本,最簡單的實現 可以用非收集區域中所有含跨代引用的對象數組來實現這個數據結構Class RemebereSetd{     Object[] setOBJECT_INTERGKENERATIONAL_REFRENCE_SIZE }

    全部記錄,無論是空間占用還是維護成本都相當高昂。

    其實在垃圾收集的場景中,收集器只需要通過記憶集判斷出某一塊非收集區域是否存在指向了收集區域的指針即可。

    在設計記憶集的時候,可以選擇更為粗獷的記錄粒度來節省記憶集的存儲和維護成本。

    字長精度每個記錄精確刀一個字長(處理器尋址位數,32/64 bit,該精度決定了機器訪問物理內存地址的指針長度),該字包含跨代指針對象精度每個記錄精確到一個對象,該對象里有字段包含跨代指針卡精度 每個記錄一塊內存區域,該區域內有對象含有跨代指針其中“卡精度”采用卡表的方式實現記憶集,目前最常用。

    卡表對記憶集的實現:

    定義了記憶集的記錄精度定義了與堆內存的映射關系

    卡表與記憶集的關系,類似于HashMap和Map的關系

    HotSpot中卡表的形式

    最簡單的一種形式:字節數組,一下代碼時默認的卡表標記邏輯

    CARD_TABLE [this address >>9] = 0;

    該數組的每一個元素都對應(注意是對應,也就是說存放的是地址)著其標志的內容區域中一塊特定大小的內存塊,該內存塊成為“卡頁”。

    一般來說 卡頁 的大小都是 2^n字節數,HotSpot使用的卡頁是2^9,也就是512字節(地址>>9 == 地址/512)

    如果卡表標識內存區域的其實地址是0x0000的話,數組CARD_TABLE的第0、1、2號元素,分別對應了地址范圍為0.00000x01FF、0x02000x03FF、0x04FF~0x05FF的卡頁內存塊.(每塊大小為512)

    一個卡頁的內存中通常包含不止一個對象,只要卡頁內有一個(或更多)對象的字段存在著跨代指針,那就將對應卡表的數組元素的值標識為1,成這個元素變臟(Dirty),沒有則標識為0。在垃圾收集發生時,只要篩選出卡表中變臟的元素,就能輕易得出哪些卡頁內存塊中包含跨代指針,把他們加入GC Roots中一并掃描。

    寫屏障

    寫屏障用來解決卡表元素維護問題。

    卡表元素何時變臟在其他分代區中對象引用了本區域對象時,其對應卡表元素就應該變臟變臟時間點原則上應該發生在引用類型字段賦值的那一刻,而處理這類問題就要用到寫屏障。

    寫屏障可以看作在虛擬機層面對”引用類型字段賦值“這個動作的AOP切面,在引用對象負值時產生一個環形通知,供程序進行額外動作,因此寫屏障分為寫前屏障與寫后屏障(將環繞通知看作前置通知+后置通知),HotSpot虛擬機很多哦收集器都有使用到寫屏障,但是直到G1收集器出現之前,都只用到了寫后屏障。

    // 寫后屏障更新卡表void oop_field_store(oop* filed,oop new_value){    //引用字段賦值操作    *field = new_value;    //寫后屏障,在這里完成卡表狀態更新    post_write_barrier(field,new_value);}

    應用寫屏障后,虛擬機就會為所有賦值操作生成相對應的指令,一旦收集器在寫屏障中增加了更新卡表操作,無論更新的是不是老年代對新生代對象的引用,每次只要是對引用進行更新,都會產生額外的開銷,不過和Minor GC是掃描整個老年代相比還是較小。

    高并發環境下

    在高并發場景下,卡表還面臨”偽共享“問題

    偽共享(False Sharing)

    處理并發底層細節時一種經常需要考慮的問題,現代代中央處理器的緩存系統中以緩存行(Cache Line)為單位存儲,當線程修改互相獨立的變量時,如果這些變量恰好共享同一個緩存行,就會彼此影響(寫回、無效化、同步)而導致性能降低,這就是偽共享問題

    偽共享相關文章: JAVA系列:緩存一致性協議( MESI協議)_NIO4444的博客-CSDN博客偽共享(False Sharing) - 知乎 (zhihu.com)真實字節二面:什么是偽共享? - 知乎 (zhihu.com) Java 偽共享的原理深度解析以及避免方法_劉Java的博客-CSDN博客【深入理解JVM】3、CPU儲存器+MESI+CPU偽共享+CPU亂序問題及代碼論證+volatile+synchrnized【面試必備】_Hello-zhou的博客-CSDN博客

    避免偽共享問題:

    不采用無條件的寫屏障,先檢查卡表標記,只有當該卡表元素未被標記時才將其標記變臟if( CARD_TABLE this address >>9 !=0 ){ CARD_TABLE this address >>9=0; }在JDK7之后,HotSpot虛擬機增加了一個新的參數 -XX:+UseCondCardMark,用來決定是否開啟卡表更新條件的判斷。開啟會增加一次額外判斷的開銷,但能夠避免偽共享問題,是否打開要根據實際運行情況來進行測試權衡。

    G1(Garbage First)

    使用場景:

    同時注重吞吐量、低延遲,默認的暫停目標時200超大堆內存,會將堆劃分為多個大小相等的Region整體上時標記+整理算法,兩個對象之間是復制算法

    相關JVM參數

    -XX:+UseG1GC-XX:G1HeapRegionSize=size-XX:MaxGCPauseMillis=time

    G1回收階段

    G1新生代回收

    標簽: 海外加速 編程算法

    上一篇:
    上一篇: