原文: "Java Garbage Collection Algorithms [till Java 9]" 垃圾回收(Garbage collection,GC)一直是 Java 流行背後的重要特性之一。垃圾回收是 Java 中用於釋放未使用記憶體的機制。本質上,它跟蹤所有仍在使用的對象,並將其餘的 ...
原文:Java Garbage Collection Algorithms [till Java 9]
垃圾回收(Garbage collection,GC)一直是 Java 流行背後的重要特性之一。垃圾回收是 Java 中用於釋放未使用記憶體的機制。本質上,它跟蹤所有仍在使用的對象,並將其餘的標記為垃圾。Java 的垃圾收集被認為是一種自動記憶體管理模式,因為程式員不必將對象指定為準備釋放的對象。垃圾回收在低優先順序線程上運行。
目錄:
對象生命周期(Object Life Cycle)
Java 的對象生命周期可以分為3個階段:
- 對象創建(Object creation),如使用關鍵字new。
- 使用中的對象(Object in use)
- 對象銷毀(Object destruction)
垃圾回收演算法(Garbage collection algorithms)
標記清除(Mark and sweep)
它是初始的和非常基本的演算法,分兩個階段運行:
- 標記存活對象(Marking live objects)– 找出所有仍然活著的對象
- 刪除不可到達的對象(Removing unreachable objects)
首先,GC 將一些特定的對象定義為垃圾收集根(Garbage Collection Roots)。 例如,當前執行方法的局部變數和輸入參數、活動線程、載入類的靜態欄位和 JNI 引用。現在,GC 遍歷記憶體中的整個對象圖,從這些根開始,然後從根引用到其他對象。 GC訪問的每個對象都被標記為活動的。
第二階段是清除未使用的對象以釋放記憶體。這可以通過多種方式來實現,例如:
- 常規刪除(Normal deletion)- 常規刪除會刪除未引用的對象以釋放空間,並留下引用的對象和指針。 記憶體分配器(類似於散列表)保存對可以分配新對象的空閑空間塊的引用。 它通常被稱為標記-清除(mark-sweep)演算法。
- 刪除-壓縮(Deletion with compacting)- 只刪除未使用的對象是沒有效率的,因為空閑記憶體塊分散在存儲區域中,如果創建的對象足夠大並且沒有找到足夠大的記憶體塊,就會導致 OutOfMemoryError 錯誤。 為瞭解決這個問題,在刪除未引用的對象之後,將對剩餘的引用對象進行壓縮。 這裡的壓縮指的是將被引用的對象移動到一起的過程。 這使得新的記憶體分配更加容易和快速。它通常被稱為標記-清除-壓縮(mark-sweep-compact)演算法。
- 複製刪除(Deletion with copying)- 與標記和壓縮的方法非常類似,因為他們也重新安置所有存活對象。 重要的區別在於遷移的目標是不同的記憶體區域。它通常被稱為標記-複製(mark-copy)演算法。
併發標記清除垃圾回收(Concurrent mark sweep (CMS) garbage collection)
它試圖通過與應用程式線程併發執行大部分垃圾收集工作來最小化垃圾收集引起的暫停。 該演算法在年輕代中採用並行stop-the-world標記複製(mark-copy)演算法,在老年代中採用大多併發的標記清除(mark-sweep)演算法。
由於從JDK9開始CMS被標記為廢棄,並且在JDK14中完全移除,因此不再介紹該收集器。
串列收集器(Serial garbage collection)
該演算法對年輕代使用標記-複製(mark-copy),對老年代使用標記-清除-壓縮(mark-sweep-compact)。它在單線程上工作。在執行時,它會凍結所有其他線程,直到垃圾回收操作結束。
由於串列垃圾收集具有線程凍結(thread-freezing)的特性,因此只適用於非常小的程式。
要使用 Serial GC,請使用以下 JVM 參數:
-XX:+UseSerialGC
並行收集器(Parallel garbage collection)
類似於串列GC,年輕代使用標記-複製(mark-copy),老年代使用標記-清除-壓縮(mark-sweep-compact)。多個併發線程用於標記和複製/壓縮階段。可以使用 -XX:ParallelGCThreads=N
選項配置線程數。
並行垃圾收集器適用於主要目標是通過有效利用現有系統資源來提高吞吐量的多核機器上。使用這種方法,可以大大減少GC的周期時間。
直到Java 8,並行收集器是預設的垃圾收集器。從 Java 9開始,G1是32位和64位伺服器配置的預設垃圾收集器。
要使用並行 GC,請使用以下 JVM 參數:
-XX:+UseParallelGC
G1(垃圾優先)收集器(G1 garbage collection)
G1(Garbage First)垃圾收集器在Java 7中可用,被設計為CMS收集器的長期替代品。 G1收集器是一個並行、併發和增量壓縮的低暫停垃圾收集器。
這種方法包括將記憶體堆分割成多個小區域(通常是2048)。每個地區被標記為年輕代(進一步分為伊甸園區域或幸存者區域)或老年代。這使得GC可以避免立即回收整個堆,而是以增量方式處理問題。這意味著一次只考慮區域的一個子集。
G1持續跟蹤每個區域包含的存活數據量。此信息用於確定包含最多垃圾的區域; 因此它們首先被回收。這就是為什麼它被命名為垃圾優先收集。
與其他演算法一樣,不幸的是,壓縮操作使用 Stop the World 方法進行。但是根據它的設計目標,你可以為它設定具體的性能目標。您可以配置暫停時間,例如,在任何給定的時間內,暫停時間不超過10毫秒。G1收集器將盡最大努力以高概率實現這一目標(但不是確定性的,由於操作系統級別的線程管理,這將是實時性的困難)。
如果你想在 Java 7 或 Java 8 機器上使用,請使用如下的 JVM 參數:
-XX:+UseG1GC
G1自定義選項
標記 | 描述 |
---|---|
-XX:G1HeapRegionSize=16m | 堆區域的大小。該值的大小為2的冪,範圍從1MB到32MB。我們的目標是根據最小的Java 堆大小設置大約2048個區域 |
-XX:MaxGCPauseMillis=200 | 為期望的最大暫停時間設置目標值。預設值是200毫秒。指定的值不適合堆大小 |
-XX:G1ReservePercent=5 | 這決定了堆中的最小儲備 |
-XX:G1ConfidencePercent=75 | 這是信心繫數暫停預測啟髮式 |
-XX:GCPauseIntervalMillis=200 | 這是每個MMU的暫停間隔時間片(以毫秒為單位) |
GC自定義選項
GC配置標記
標記 | 描述 |
---|---|
-Xms2048m -Xmx3g | 設置初始和最大堆大小(年輕代+老年代) |
-XX:+DisableExplicitGC | 這將導致JVM忽略應用程式對System.gc() 方法的任何調用。 |
-XX:+UseGCOverheadLimit | This is the use policy used to limit the time spent in garbage collection before an OutOfMemory error is thrown. |
-XX:GCTimeLimit=95 | This limits the proportion of time spent in garbage collection before an OutOfMemory error is thrown. This is used with GCHeapFreeLimit. |
-XX:GCHeapFreeLimit=5 | This sets the minimum percentage of free space after a full garbage collection before an OutOfMemory error is thrown. This is used with GCTimeLimit. |
-XX:InitialHeapSize=3g | 設置初始堆空間(年輕區+老年區) |
-XX:MaxHeapSize=3g | 設置最大堆空間(年輕區+老年區) |
-XX:NewSize=128m | 設置年輕區的初始空間 |
-XX:MaxNewSize=128m | 設置年輕區的最大空間 |
-XX:SurvivorRatio=15 | 設置單個幸存者空間的大小為伊甸園空間大小的一部分 |
-XX:PermSize=512m | 設置永久區的初始空間 |
-XX:MaxPermSize=512m | 設置永久區的最大空間 |
-Xss512k | 設置專用於每個線程的棧區域的大小(以位元組為單位) |
GC日誌標記
標記 | 描述 |
---|---|
-verbose:gc or -XX:+PrintGC | This prints the basic garbage collection information. |
-XX:+PrintGCDetails | This will print more detailed garbage collection information. |
-XX:+PrintGCTimeStamps | You can print timestamps for each garbage collection event. The seconds are sequential and begin from the JVM start time. |
-XX:+PrintGCDateStamps | You can print date stamps for each garbage collection event. |
-Xloggc: | Using this you can redirect garbage collection output to a file instead of the console. |
-XX:+Print\TenuringDistribution | You can print detailed information regarding young space following each collection cycle. |
-XX:+PrintTLAB | You can use this flag to print TLAB allocation statistics. |
-XX:+PrintReferenceGC | Using this flag, you can print the times for reference processing (that is, weak, soft, and so on) during stop-the-world pauses. |
-XX:+HeapDump\OnOutOfMemoryError | This creates a heap dump file in an out-of-memory condition. |
總結
- 對象生命周期分成3個階段,對象創建,對象使用和對象銷毀。
- 標記-清除、標記-清除-壓縮、標記-複製機制如何工作。
- 不同的單線程和併發垃圾回收演算法。
- 直到Java 8, 併發收集器是預設演算法。
- 從Java 9開始, G1是預設演算法。
- 此外,還有各種標誌,用於控制垃圾收集演算法的行為並記錄任何應用程式的有用信息