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()会比较好。

目录
相关文章
|
缓存 Java
Android--SoftReference缓存图片
Android--SoftReference缓存图片
258 2
|
存储 Java 程序员
Android的内存泄露
对于开发老手,这个问题想必已经深入你的心;若是一名新手或者一直对内存泄漏这个东西模模糊糊的工程师,你的答案可能让面试官并不满意,这里将从底到上对内存泄漏的原因、排查方法和一些经验为你做一次完整的解剖。 处理内存泄漏的问题是将软件做到极致的一个必须的步骤,尤其是那种将被用户高强度使用的软件。
147 0
Android的内存泄露
|
Java Android开发
Android Runtime.getRuntime().freeMemory()会返回0吗?
### 背景 上周基于[LruCache](https://developer.android.com/reference/android/util/LruCache.html)实现了一个缓存,实现逻辑大概如下: ```java //使用剩余内存的1/16作为缓存池的最大值 int size = Runtime.getRuntime().freeMemory() / 16; Lru
2188 0
|
传感器 Java Android开发
八个造成 Android 应用内存泄露的原因
本文讲的是八个造成 Android 应用内存泄露的原因,诸如 Java 这样的 GC (垃圾回收)语言的一个好处就是免去了开发者管理内存分配的必要。这样降低了段错误导致应用崩溃或者未释放的内存挤爆了堆的可能性,因此也能编写更安全的代码。
1121 0
|
缓存 Java Android开发