今天发现个有趣的现象, 点击DDMS Monitor的initiate gc按钮和调用System.gc的效果不同。 ”initiate gc“按钮的效果更明显, 即内存下降更多。 我在想这个”initiate gc“是怎么做到的???
PS: 代码调用gc实际上是给虚拟机发个信号, 并不能立刻执行gc; 而Android Studio的这个按钮会马上执行gc! 所有在检查内存泄漏后要点击“initate gc”按钮, 该按钮能释放不可达对象。
VS 在Activity的onDestory函数中显示触发gc。
@Override
protected void onDestroy() {
super.onDestroy();
Runtime.getRuntime().gc();
}
备注:Runtime.getRuntime.gc()和System.gc()可以理解为一个东西, 只是写法不同而已。
从二者的日志开始分析, 使用小米4手机、MIUI8、Android6.0系统测试。 点击“Initiate gc"或执行System.gc都会显示下面2行日志。
12-06 14:53:35.624 I/art ( 5967): Starting a blocking GC Explicit
12-06 14:53:35.664 I/art ( 5967): Explicit concurrent mark sweep GC freed 3962(256KB) AllocSpace objects, 15(28MB) LOS objects, 40% free, 65MB/108MB, paused 440us total 39.442ms
这里的cause就是字符串Explicit, 在gc_cause.cc里定义。
gc_cause.cc定义了12种gc的理由。 我们使用的System.gc和Runtime.getRuntime.gc的枚举类型是kGcCauseExplicit。
// What caused the GC?
enum GcCause {
// GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
// retrying allocation.
kGcCauseForAlloc,
// A background GC trying to ensure there is free memory ahead of allocations.
kGcCauseBackground,
// An explicit System.gc() call.
kGcCauseExplicit,
// GC triggered for a native allocation.
kGcCauseForNativeAlloc,
// GC triggered for a collector transition.
kGcCauseCollectorTransition,
// Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
kGcCauseDisableMovingGc,
// Not a real GC cause, used when we trim the heap.
kGcCauseTrim,
// Not a real GC cause, used to implement exclusion between GC and instrumentation.
kGcCauseInstrumentation,
// Not a real GC cause, used to add or remove app image spaces.
kGcCauseAddRemoveAppImageSpace,
// Not a real GC cause, used to implement exclusion between GC and debugger.
kGcCauseDebugger,
// GC triggered for background transition when both foreground and background collector are CMS.
kGcCauseHomogeneousSpaceCompact,
// Class linker cause, used to guard filling art methods with special values.
kGcCauseClassLinker,
};
clear_soft_references为true或false时释放的内存空间会不同, 所以推测DDMS Monitor的“initiate gc”重写了native层添加了对应接口。
补充: https://github.com/TencentOpen/GT/blob/master/android/src/com/tencent/wstt/gt/proInfo/floatView/GTMemHelperFloatview.java
如果有root权限, 执行adb shell kill -10 pid也可以调用GC, 在linux kill命令中状态10表示SIGUSR1,调用的是signal_catcher.cc的HandleSigUrs1函数。
void SignalCatcher::HandleSigUsr1() {
LOG(INFO) << "SIGUSR1 forcing GC (no HPROF)";
Runtime::Current()->GetHeap()->CollectGarbage(false);
}