在看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的源码:
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,是这样描述的:
从这里可以得到两点信息:
1.首先这是为了修复一个bug 12004934,具体什么bug找不到了
2.其次在art模式下,直接调用gc的效果不大。至于为什么,还没有深入进去了解,这是ART相关的另外一个专题了,后面再详细跟进。
回到开头,leakcanary的作者在这里直接用了Runtime.getRuntime().gc()的确是有理由的,但是这应该不是最好的方式,因为从这个提交的描述来看,连续调用Runtime.getRuntime().gc()可能存在bug。修改后的模式是gc / finalization / gc,虽然leakcanary这里的使用不会有问题。但是我觉得我们自己使用的话,用System.gc() 配合 System.runFinalization()会比较好。