开源框架剖析-内存leakcanary
内存泄露对外不可见 leakcanary是Square开源的一款轻量第三方内存泄露检测工具,原理是监控一个即将要销毁的对象
内存泄露主要发生的部位有
1,栈stack 基本类型,对象引用
2,堆heap
3,方法区method
为什么会有内存泄露
1,当一个对象已经不需要再使用了
2,有些对象只有有限的生命周期
内存泄露会导致什么问题
OOM,程序崩溃
常见的内存泄露
1,单例造成的内存泄露
单例生命周期和应用一样长,如果持有一个Activity类型的Context,如果activity退出后,就造成无法回收
解决办法:弄一个Application的Context。context.getApplicationContext()
2,非静态内部类创建静态实例
此时肥静态内部类对象持有外部类的引用,导致外部类不能释放
3,Handler造成的内存泄露。handler,Message,MessageQueue
TLS,Handler生命周期和Activity不一致。Message持有handler,handler持有Activity,从而导致内存泄露
解决办法:将Handler声明为静态,从而将handler和activity生命周期解绑;通过弱引用方式引入Activity;activity销毁时移除handler回调
4,子线程造成的内存泄露
定义子线程时,没有使用静态内部类导致持有外部类引用
解决办法:定义成静态的,从而不持有外部;在销毁时,取消掉任务。
5,WebView造成的内存泄露
WebView加载Html页面,会申请Native堆内存来保存页面,内存占用严重。关闭WebView也不能释放。
解决办法:将WebView放在一个单独进程,当不再使用时,killProgress杀死进程
6,动画导致的内存泄露
6,集合对象未及时清理导致的内存泄露
LeakCanary原理
1,Activity Destory之后将它放在一个WeakReference
2,将这个WeakReference关联到一个ReferenceQueue
3,查看ReferenceQueue是否存在Activity的引用
4,如果该Activity泄露了,Dump出堆Heap信息,然后再去分析泄露路径。本质上还是用命令控制生成hprof文件分析检查内存泄露,然后发送通知。
软引用/弱引用的对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中。
LeakCanary如何检测内存泄露
1,首先创建一个refwatcher,启动一个ActivityRefWatcher
Application
LeakCanary.install()
最终调用到
ActivityRefWatcher
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
2,通过ActivityLifecycleCallbacks把Activity的onDestory生命周期管理
ActivityLifecycleCallbacks
@Override
public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
ActivityRefWatcher
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
3,最后延时5s后开始分析内存泄露
RefWatcher
public void watch(Object watchedReference, String referenceName) {
...
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =//这是一个弱引用
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
//通过addIdleHandler获取消息闲时时机,默认延迟5秒后回调此runnable
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
//5秒延时之后的逻辑
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();//移除没有泄露的reference
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {//通过比对retainedKeys和reference判断有没有泄露
return DONE;
}
gcTrigger.runGc();//再给一次GC机会
removeWeaklyReachableReferences();
if (!gone(reference)) {//GC后还是没被回收,那就是有泄露了
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();//dump出hprof文件
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(//去分析内存泄露
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
ServiceHeapDumpListener
public void analyze(HeapDump heapDump) {//开启一个IntentService去分析
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
public final class HeapAnalyzerService extends IntentService
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
//去走checkForLeak方法
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
4,HeapAnalyzer.checkForLeak方法,是最重要的一个方法,主要做以下操作。用到了HaHa库去分析内存泄露
1),解析hprof转为Snapshot内存快照
2),优化gcroots
3),找出泄露的对象findLeakingReference/找出泄露对象的最短路径findLeakTrace ``` HeapAnalyzer
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
//1,解析hprof转为Snapshot内存快照
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
Snapshot snapshot = parser.parse();
//2,优化gcroots
deduplicateGcRoots(snapshot);
//找出泄露的对象findLeakingReference方法
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
//找出泄露对象的最短路径findLeakTrace
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
} ```
5,最后调用DisplayLeakService(extends AbstractAnalysisResultService)发送通知