-XX:+DisableExplicitGC弊端

總結:

如果jvm參數中設置了-XX:+DisableExplicitGC,那麼代碼中手動調用System.gc()就不會生效。而有些框架中因爲是使用的堆外內存,必須手動調用System.gc()來釋放。如果禁用掉就會導致堆外內存使用一直增長,造成內存泄露。

 

詳解:

直接內存與System.gc()

System.gc()默認會觸發一次Full GC,如果在代碼中不小心調用了System.gc()會導致JVM間歇性的暫停,但有些NIO框架
比如Netty框架經常會使用DirectByteBuffer來分配堆外內存,在分配之前會顯式的調用System.gc(),如果開啓了DisableExplicitGC
這個參數,會導致System.gc()調用變成一個空調用,沒有任何作用,反而會導致Netty框架無法申請到足夠的堆外內存,從而產生
java.lang.OutOfMemoryError: Direct buffer memory

既然是堆外內存,爲什麼觸發Full GC會有助於回收堆外內存呢,Full GC不是隻回收JVM的堆內存嗎?這就要了解下DirectByteBuffer的回收機制了,DirectByteBuffer沒有finalizer,它的native memory的清理工作是通過sun.misc.Cleaner自動完成的,而sum.misc.Cleaner是一種基於虛引用的回收工具,從JDK源碼也可以看到:         

public class Cleaner extends PhantomReference<Object>

當GC檢查到Cleaner的引用變成虛引用可達時,reference-handler線程會調用Cleaner的clean方法回收內存,這個機制可以在
java.lang.ref.Reference$ReferenceHandler裏看到,Reference類加載的時候會創建reference-handler線程:                                     

public void run() {
            for (;;) {
                //省略了一些
                // Fast path for cleaners
                if (r instanceof Cleaner) {
                    ((Cleaner)r).clean();
                    continue;
                }
            }
static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();
    }

JVM在做Full GC時會對引用作處理(reference processing),當GC檢測到Cleaner的引用變成虛可達時,引用Handler線程會觸發Cleaner對DirectByteBuffer對象作清理工作.

CMS垃圾回收器下的推薦配置

既然不推薦使用DisableExplicitGC這個參數,那有沒有什麼辦法能儘量減少顯式調用System.gc()帶來的GC停頓呢,JVM提供了
ExplicitGCInvokesConcurrent和ExplicitGCInvokesConcurrentAndUnloadsClasses這兩個參數來保證顯式調用System.gc()
觸發的是一個併發GC週期而不是Full GC,這兩個參數只能配合CMS使用(-XX:+UseConcMarkSweepGC):

CMS GC週期內也會做reference-processing,因此也能夠觸發對DirectByteBuffer內存的回收,減少了Full GC帶來的長時間
停頓。

當沒有開啓DisableExplicitGC這個參數時,你會發現JVM每個小時會執行一次Full GC,這是因爲JVM在做分佈式GC,爲RMI服務的,
可以通過sun.rmi.dgc.server.gcInterval這個參數來修改GC間隔,默認是一個小時。                  

System.gc常識

  • system.gc其實是做一次full gc
  • system.gc會暫停整個進程
  • system.gc一般情況下我們要禁掉,使用-XX:+DisableExplicitGC
  • system.gc在cms gc下我們通過-XX:+ExplicitGCInvokesConcurrent來做一次稍微高效點的GC(效果比Full GC要好些)
  • system.gc最常見的場景是RMI/NIO下的堆外內存分配等

參考:http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章