Java 12 中增強了 G1 垃圾收集器關于混合收集集合的處理策略,這節主要介紹在 Java 12 中同時也對 G1垃圾回收器進行了改進,使其能夠在空閑時自動將 Java 堆內存返還給操作系統,這也是 Java 12 中的另外一項重大改進。
目前 Java 11 版本中包含的 G1 垃圾收集器暫時無法及時將已提交的 Java 堆內存返回給操作系統。為什么呢? G1目前只有在full GC或者concurrent cycle(并發處理周期)的時候才會歸還內存,由于這兩個場景都是G1極力避免的,因此在大多數場景下可能不會及時歸還committed Java heap memory給操作系統。除非有外部強制執行。
在使用云平臺的容器環境中,這種不利之處特別明顯。即使在虛擬機不活動,但如果仍然使用其分配的內存資源,哪怕是其中的一小部分,G1 回收器也仍將保留所有已分配的 Java 堆內存。而這將導致用戶需要始終為所有資源付費,哪怕是實際并未用到,而云提供商也無法充分利用其硬件。如果在此期間虛擬機能夠檢測到 Java 堆內存的實際使用情況,并在利用空閑時間自動將 Java 堆內存返還,則兩者都將受益。
具體操作
為了盡可能的向操作系統返回空閑內存,G1 垃圾收集器將在應用程序不活動期間定期生成或持續循環檢查整體 Java堆使用情況,以便 G1 垃圾收集器能夠更及時的將 Java 堆中不使用內存部分返還給操作系統。對于長時間處于空閑狀態的應用程序,此項改進將使 JVM 的內存利用率更加高效。
而在用戶控制下,可以可選地執行Full GC,以使返回的內存量最大化。
JDK12的這個特性新增了兩個參數分別是G1 PeriodicGCInterval及G1 PeriodicGCSystemLoadThreshold,設置為0的話,表示禁用。如果應用程序為非活動狀態,在下面兩種情況任何一個描述下,G1 回收器會觸發定期垃圾收集:
自上次垃圾回收完成以來已超過 G1PeriodicGCInterval ( milliseconds ), 并且此時沒有正在進行的垃圾回收
任務。如果 G1PeriodicGCInterval 值為零表示禁用快速回收內存的定期垃圾收集。
應用所在主機系統上執行方法 getloadavg(),默認一分鐘內系統返回的平均負載值低于
G1PeriodicGCSystemLoadThreshold指定的閾值,則觸發full GC或者concurrent GC( 如果開啟
G1PeriodicGCInvokesConcurrent ),GC之后Java heap size會被重寫調整,然后多余的內存將會歸還給操作系統。如果 G1PeriodicGCSystemLoadThreshold 值為零,則此條件不生效。
如果不滿足上述條件中的任何一個,則取消當期的定期垃圾回收。等一個 G1PeriodicGCInterval 時間周期后,將重新考慮是否執行定期垃圾回收。
G1 定期垃圾收集的類型根據 G1PeriodicGCInvokesConcurrent 參數的值確定:如果設置值了,G1 垃圾回收器將繼續上一個或者啟動一個新并發周期;如果沒有設置值,則 G1 回收器將執行一個Full GC。在每次一次 GC 回收末尾,G1 回收器將調整當前的 Java 堆大小,此時便有可能會將未使用內存返還給操作系統。新的 Java 堆內存大小根據現有配置確定,具體包括下列配置:- XX:MinHeapFreeRatio、-XX:MaxHeapFreeRatio、-Xms、-Xmx。
默認情況下,G1 回收器在定期垃圾回收期間新啟動或繼續上一輪并發周期,將最大限度地減少應用程序的中斷。如果定期垃圾收集嚴重影響程序執行,則需要考慮整個系統 CPU 負載,或讓用戶禁用定期垃圾收集。