Android System.gc()注意点

5051 ワード

背景
square Leakcanaryソースコードを見ていると、次のような言葉が見つかりました.
GcTrigger DEFAULT = new GcTrigger() {
    @Override public void runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perfom a gc.
      Runtime.getRuntime().gc();
      enqueueReferences();
      System.runFinalization(); 
}

フォローアップ
いったい何が違うのでしょうか.手元の4.2.2とopenjdkのソースコードを見ました.
public static void gc() {
        Runtime.getRuntime().gc();
}

System.gc()の実装はRuntimeを呼び出すことである.getRuntime().gc()なので、両者は等価です.だからここは作者が心配しているのではないでしょうか.また5.0のソースコードを見てみましたが、やはり違いました.

/**
 * Whether or not we need to do a GC before running the finalizers.
 */
  private static boolean runGC;

  /**
   * If we just ran finalization, we might want to do a GC to free the finalized objects.
   * This lets us do gc/runFinlization/gc sequences but prevents back to back System.gc().
   */
  private static boolean justRanFinalization;


/**
    * Provides a hint to the VM that it would be useful to attempt
    * to perform any outstanding object finalization.
    */
    public static void runFinalization() {
        boolean shouldRunGC;
        synchronized(lock) {
            shouldRunGC = runGC;
            runGC = false;
        }
        if (shouldRunGC) {
            Runtime.getRuntime().gc();
        }
        Runtime.getRuntime().runFinalization();
        synchronized(lock) {
            justRanFinalization = true;
        }
    }


 /**
   * Indicates to the VM that it would be a good time to run the
   * garbage collector. Note that this is a hint only. There is no guarantee
   * that the garbage collector will actually be run.
   */
  public static void gc() {
      boolean shouldRunGC;
      synchronized(lock) {
          shouldRunGC = justRanFinalization;
          if (shouldRunGC) {
              justRanFinalization = false;
        } else {
            runGC = true;
          }
    }
   if (shouldRunGC) {
          Runtime.getRuntime().gc();
   } 
}

このように変更する、単純にSystemを呼び出す.gc()はRuntimeをトリガーしない.getRuntime().gc()の.しかし、今度の試みを記録して、次回Systemを呼び出すまで待っています.RunFinalization()の場合、このRuntimeが先に実行されます.getRuntime().gc(). このような変更の影響は少なくとも2つある:1.単純にシステムを呼び出す.gc()はRuntimeをトリガーしない.getRuntime().Systemが呼び出されるまでgc()runFinalization() 2.System.gc() -> System.gc() -> … -> System.gc() ->System.RunFinalization()は、最終的にはRuntimeを1回だけ呼び出す.getRuntime().gc()
どうしてこのように変えたのですか.このcommitが見つかりました.
Avoid running Runtime.gc() until we need to run finalization.
This prevents excessive explicit GC which are called from apps to get good GC behavior on Dalvik. Calling System.gc() does not help on ART since GC for alloc is much rarer.
If running finalizers is requested following a System.gc we remember that a GC was requested and perform it ahead of finalization.
Bug: 12004934
ここから2つの情報が得られます:1.まずこれは1つのバグ12004934を修復するためで、具体的には何のバグが見つからない.次にartモードでは,gcを直接呼び出す効果は大きくない.なぜかについては、まだ深く理解していませんが、これはARTに関するもう一つのテーマで、後で詳しくフォローします.
冒頭に戻ると、leakcanaryの作者はここでRuntimeを直接使った.getRuntime().gc()には確かに理由があるが,これは最良の方法ではないはずである.このコミットの記述から見るとRuntimeが連続的に呼び出されるからである.getRuntime().gc()にバグがある可能性があります.修正後のモードはgc/finalization/gcですが、leakcanaryでは問題ありません.でも私たちが自分で使うならSystemを使うと思います.gc()はSystemに合わせる.runFinalization()がいいです.