淺談Java的System.gc()實現

我們都知道System.gc()用於調用垃圾收集器。很久之前我一直認爲執行System.gc()之後,虛擬機會立刻垃圾回收。

抱歉,我理解錯了。


直到看完System.gc()的源碼之後才搞清楚,執行System.gc()函數的作用只是提醒或告訴虛擬機,希望進行一次垃圾回收。
至於什麼時候進行回收還是取決於虛擬機,而且也不能保證一定進行回收(如果-XX:+DisableExplicitGC設置成true,則不會進行回收)。

然後在簡單說一下什麼樣的對象會被gc()回收:sum的hotspot虛擬機是通過可達性分析算法來判斷對象是否需要回收。這裏不重點講述可達性分析算法,之後在單獨寫一篇博客來介紹。

先來簡單看一下System.gc()的源碼吧:

        /**
         * Runs the garbage collector.
         * <p>
         * Calling the <code>gc</code> method suggests that the Java Virtual
         * Machine expend effort toward recycling unused objects in order to
         * make the memory they currently occupy available for quick reuse.
         * When control returns from the method call, the Java Virtual
         * Machine has made a best effort to reclaim space from all discarded
         * objects.
         * <p>
         * The call <code>System.gc()</code> is effectively equivalent to the
         * call:
         * <blockquote><pre>
         * Runtime.getRuntime().gc()
         * </pre></blockquote>
         *
         * @see     java.lang.Runtime#gc()
         */
        public static void gc() {
            Runtime.getRuntime().gc();
        }

簡單看一下gc()函數的註釋(翻譯的不對的地方望見諒,水平有限)
Runs the garbage collector.

Calling the <code>gc</code> method suggests that the Java Virtual
Machine expend effort toward recycling unused objects in order to
make the memory they currently occupy available for quick reuse.
When control returns from the method call, the Java Virtual
Machine has made a best effort to reclaim space from all discarded
objects.

運行垃圾收集器。

調用gc()函數表明Java虛擬機花費了很多精力來回收未使用的對象,以使它們當前佔用的內存可用於快速重用。
當控制從方法調用返回時,Java虛擬機已盡最大努力從所有丟棄的對象中回收空間。

在註釋中也明確的說明了,調用System.gc()等同於調用Runtime.getRuntime().gc()。

好吧,我們接着來分析Runtime.getRuntime().gc():

        /**
         * Runs the garbage collector.
         * Calling this method suggests that the Java virtual machine expend
         * effort toward recycling unused objects in order to make the memory
         * they currently occupy available for quick reuse. When control
         * returns from the method call, the virtual machine has made
         * its best effort to recycle all discarded objects.
         * <p>
         * The name <code>gc</code> stands for "garbage
         * collector". The virtual machine performs this recycling
         * process automatically as needed, in a separate thread, even if the
         * <code>gc</code> method is not invoked explicitly.
         * <p>
         * The method {@link System#gc()} is the conventional and convenient
         * means of invoking this method.
         */
        public native void gc();

好吧,原來gc()是native方法,在java層面只能看到這麼多了,先來看一下注釋的意思吧。

意思和System.gc()的註釋差不多,需要注意的是,在註解中也說明了,需要在單獨的線程自動執行。

正好我電腦上有OpenJDK8的代碼,我們接着去Hotspot裏看看System.gc()是怎麼實現的吧

先下載OpenJDK的源碼,我下載的是OpenJDK8的源碼,在jdk/src/share/native/java/lang 目錄中有一個 Runtime.c 文件

    JNIEXPORT void JNICALL
    Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
    {
        JVM_GC();
    }

沒錯就是這個函數(抱歉,我不能解釋我是怎麼知道就是這個函數的,因爲我是根據名稱推測的。。。),很簡單,只是調用了JVM_GC這個函數。

那我們接着看JVM_GC這個函數實現的邏輯吧,其實我不用C和C++,也是諮詢了JVM大神才定位到JVM_GC函數的定義的。

在hotspot/src/share/vm/prims 目錄中有一個 jvm.cpp 文件

    JVM_ENTRY_NO_ENV(void, JVM_GC(void))
      JVMWrapper("JVM_GC");
      if (!DisableExplicitGC) {
        Universe::heap()->collect(GCCause::_java_lang_system_gc);
      }
    JVM_END

其實我不是太能看懂,我只能帶着我的疑問一步步來推測了,DisableExplicitGC 是在哪定義的呢?默認是true還是false呢?

來跟大家說一下怎麼找到 DisableExplicitGC 這個函數的定義:在OpenJDK目錄下執行 grep -nr "DisableExplicitGC" . 。唉,因爲不會,所以只能用這麼笨的辦法了,見笑。

在hotspot/src/share/vm/runtime 目錄中有一個 globals.hpp 文件,這裏面就有 DisableExplicitGC 的定義

    product(bool, DisableExplicitGC, false,
              "Ignore calls to System.gc()")

好了,這個疑問解決了,默認設置成了false,所以在不修改 DisableExplicitGC的情況下,會執行 if 裏面的代碼的。接着分析 if 裏面的代碼啦。

調用了collect()函數,所以我們看一下這個函數的實現:

    void GenCollectedHeap::collect(GCCause::Cause cause) {
      if (should_do_concurrent_full_gc(cause)) {
    #if INCLUDE_ALL_GCS
        // mostly concurrent full collection
        collect_mostly_concurrent(cause);
    #else  // INCLUDE_ALL_GCS
        ShouldNotReachHere();
    #endif // INCLUDE_ALL_GCS
      } else if (cause == GCCause::_wb_young_gc) {
        // minor collection for WhiteBox API
        collect(cause, 0);
      } else {
    #ifdef ASSERT
      if (cause == GCCause::_scavenge_alot) {
        // minor collection only
        collect(cause, 0);
      } else {
        // Stop-the-world full collection
        collect(cause, n_gens() - 1);
      }
    #else
        // Stop-the-world full collection
        collect(cause, n_gens() - 1);
    #endif
      }
    }

    bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
      return UseConcMarkSweepGC &&
             ((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
              (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent));
    }

如果should_do_concurrent_full_gc返回true,那會執行collect_mostly_concurrent做並行的回收,should_do_concurrent_full_gc的實現也就不在多說了,代碼貼出來了。有興趣的朋友可以繼續跟蹤下去。

發佈了23 篇原創文章 · 獲贊 3 · 訪問量 3726
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章