Android System.gc()注意点

背景

在看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()。
这样改后的影响至少有两点:
1.单纯调用System.gc()是不会触发Runtime.getRuntime().gc()的,直到调用了System.runFinalization()
2.System.gc() -> System.gc() -> … -> System.gc() ->System.runFinalization(),最终只会调用一次Runtime.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

从这里可以得到两点信息:
1.首先这是为了修复一个bug 12004934,具体什么bug找不到了
2.其次在art模式下,直接调用gc的效果不大。至于为什么,还没有深入进去了解,这是ART相关的另外一个专题了,后面再详细跟进。

回到开头,leakcanary的作者在这里直接用了Runtime.getRuntime().gc()的确是有理由的,但是这应该不是最好的方式,因为从这个提交的描述来看,连续调用Runtime.getRuntime().gc()可能存在bug。修改后的模式是gc / finalization / gc,虽然leakcanary这里的使用不会有问题。但是我觉得我们自己使用的话,用System.gc() 配合 System.runFinalization()会比较好。

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值