1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > android leakcanary 源码分析 LeakCanary源码浅析

android leakcanary 源码分析 LeakCanary源码浅析

时间:2018-09-07 15:52:54

相关推荐

android leakcanary 源码分析 LeakCanary源码浅析

在Android开发中最让人们头疼的就是内存泄漏了,今天来介绍一个查看内存是否泄漏的工具LeakCanary,并通过研究源码明白它是如何分析和查找存在泄漏信息的

在Android开发中最让人们头疼的就是内存泄漏了,今天来介绍一个查看内存是否泄漏的工具LeakCanary,并通过研究源码明白它是如何分析和查找存在泄漏信息的

首先送上LeakCanary文档链接:LeakCanary中文使用说明

Part1. 知识回顾常用工具Mat

LeakCanary(Square)

原理:watch监视一个即将要销毁的对象内存种类1、栈(stack-基本数据类型,对象的引用)

2、堆(heap-存放new出来的对象和数组,在堆中分配内存由GC管理)

3、方法区(method,大体和堆一样)为什么会产生内存泄漏当一个对象已经不需要再使用了,在该对象被回收时候,有另外的对象引用该回收对象,导致本该被回收的对象无法回收

有些对象只有有限的生命周期,当生命周期很短的完成任务后,在本该结束的生命周期中仍然被引用内存泄漏会导致什么问题OOM常见的内存泄漏情况单例造成的内存泄漏

非静态内部类创建静态实例造成的内存泄漏

handler造成内存泄漏(handler、message、MessageQueue)

解决方法:①将Handler声明为静态类型

②通过弱引用的方式引入Activity

线程造成的内存泄漏(解决方法:将线程定义声明为static类型)

webview造成的内存泄漏(example:加载页面很复杂,Ex:大量的图片)

Part2 概念引用类型强引用(StrongReference),默认对象一般为强引用

软引用(SoftReference),当内存空间足够大时相当于强引用,内存不够时通过垃圾回收器(GC)自动回收

弱引用(WeakReference),当GC扫描到该类型的引用时就自己回收

虚引用,相当于没有进行引用,GC可随时回收该类型的引用ReferenceQueue软引用和弱引用都持有该对象

对象被垃圾回收,Java虚拟机就会把这个引用加入到与之相关联的引用队列中

Part3.LeakCanary使用在module层级中的build.gradle中加入引用,不同的编译使用不同的引用dependencies{

debugCompile'com.squareup.leakcanary:leakcanary-android:1.3'

releaseCompile'com.squareup.leakcanary:leakcanary-android-no-op:1.3'}

2.在Application中:publicclassMyApplicationextendsApplication{@Override

publicvoidonCreate(){super.onCreate();

LeakCanary.install(this);

}

}

3.在Manifest.xml中加载该Application文件

Part4. LeakCanary源码剖析

从代码入口剖析:LeakCanary.install(this);

跟踪源码可知/**

*Createsa{@linkRefWatcher}thatworksoutofthebox,andstartswatchingactivity

*references(onICS+).

*/

publicstaticRefWatcherinstall(Applicationapplication){returninstall(application,DisplayLeakService.class);

}

从上面的代码我们发现这个方法最终返回给我们一个RefWatcher这个类,这个类是主要是启动ActivityRefWatcher类,ActivityRefWatcher在Activity的onDestory方法结束时检测内存泄漏。

看下install这个方法:/**

*Createsa{@linkRefWatcher}thatreportsresultstotheprovidedservice,andstartswatching

*activityreferences(onICS+).

*/

publicstaticRefWatcherinstall(Applicationapplication,ClassextendsAbstractAnalysisResultService>listenerServiceClass){if(isInAnalyzerProcess(application)){returnRefWatcher.DISABLED;

}

enableDisplayLeakActivity(application);

HeapDump.ListenerheapDumpListener=newServiceHeapDumpListener(application,listenerServiceClass);

RefWatcherrefWatcher=androidWatcher(application,heapDumpListener);

ActivityRefWatcher.installOnIcsPlus(application,refWatcher);returnrefWatcher;

}

通过RefWatcher refWatcher = androidWatcher(application, heapDumpListener)创建一个RefWatcher对象,启动activityRefWatcher来监视内存泄漏

enableDisplayLeakActivity(application)主要作用是开启DisplayLeakActivity这个类,这个类主要是显示内存泄漏的弹框页面ActivityRefWatcher.installOnIcsPlus(application,refWatcher);publicstaticvoidinstallOnIcsPlus(Applicationapplication,RefWatcherrefWatcher){if(SDK_INT

return;

}

ActivityRefWatcheractivityRefWatcher=newActivityRefWatcher(application,refWatcher);

activityRefWatcher.watchActivities();

}publicvoidwatchActivities(){//Makesureyoudon'tgetinstalledtwice.

stopWatchingActivities();

application.registerActivityLifecycleCallbacks(lifecycleCallbacks);

}publicvoidstopWatchingActivities(){

application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);

}

小结:

①通过stopWatcher方法反注册以前的Activity的生命周期的callback,目的是为了保证以前的内存泄漏的activity删除

②重新注册activity生命周期的callback

③通过lifecycleCallbacks中的onActivityDestroyed方法将activity的生命周期和ActivityReference关联起来@Override

publicvoidonActivityDestroyed(Activityactivity){

ActivityRefWatcher.this.onActivityDestroyed(activity);

}

跟踪onActivityDestroyed方法发现通过调用RefWatcher调用了watcher类voidonActivityDestroyed(Activityactivity){

refWatcher.watch(activity);

}privatefinalRefWatcherrefWatcher

下面我们进入RefWatcher类中发现有如下的变量信息privatefinalExecutorwatchExecutor;privatefinalDebuggerControldebuggerControl;privatefinalGcTriggergcTrigger;privatefinalHeapDumperheapDumper;privatefinalSetretainedKeys;privatefinalReferenceQueuequeue;privatefinalHeapDump.ListenerheapdumpListener;

上述变量大意如下:watchExecutor主要用于执行内存泄漏检测

debuggerControl查询我们是否正在调试中,如果我们正在调试过程中则不会进行判断

gcTrigger用于处理GC,用于在判断泄漏对象之前再调用GC类中的方法再次判断

heapDumper用于dump中内存泄漏堆文件

retainedKeys该set集合持有待检测和已产生内存泄漏信息的key

queue引用对象,主要是判断弱引用所持有的对象是否已执行GC垃圾收回

heapdumpListener主要用于分析产生hprof文件回调

查看watch方法可知:/**

*WatchestheprovidedreferencesandchecksifitcanbeGCed.Thismethodisnonblocking,

*thecheckisdoneonthe{@linkExecutor}this{@linkRefWatcher}hasbeenconstructedwith.

*

*@paramreferenceNameAnlogicalidentifierforthewatchedobject.

*/

publicvoidwatch(ObjectwatchedReference,StringreferenceName){

checkNotNull(watchedReference,"watchedReference");

checkNotNull(referenceName,"referenceName");if(debuggerControl.isDebuggerAttached()){return;

}finallongwatchStartNanoTime=System.nanoTime();

Stringkey=UUID.randomUUID().toString();

retainedKeys.add(key);finalKeyedWeakReferencereference=newKeyedWeakReference(watchedReference,key,referenceName,queue);

watchExecutor.execute(newRunnable(){@Overridepublicvoidrun(){

ensureGone(reference,watchStartNanoTime);

}

});

}

通过产生一个唯一的key添加到retainedKeys集合中Stringkey=UUID.randomUUID().toString();

retainedKeys.add(key);

再创建一个KeyedWeakReference的弱引用,并开启一个异步线程来分析创建好的弱引用,该线程主要作用是确保我们的Activity是否真正已经进入到GONE状态voidensureGone(KeyedWeakReferencereference,longwatchStartNanoTime){longgcStartNanoTime=System.nanoTime();

//计算过去的方法到调用GC垃圾收回的时间值

longwatchDurationMs=NANOSECONDS.toMillis(gcStartNanoTime-watchStartNanoTime);//清除已经到达我们引用队列的弱引用

removeWeaklyReachableReferences();//判断如果处于debug状态就不再进行内存分析

if(gone(reference)||debuggerControl.isDebuggerAttached()){return;

}

gcTrigger.runGc();//手动进行垃圾回收

removeWeaklyReachableReferences();if(!gone(reference)){longstartDumpHeap=System.nanoTime();longgcDurationMs=NANOSECONDS.toMillis(startDumpHeap-gcStartNanoTime);

FileheapDumpFile=heapDumper.dumpHeap();//dump出内存泄漏的文件

if(heapDumpFile==null){//Couldnotdumptheheap,abort.

return;

}longheapDumpDurationMs=NANOSECONDS.toMillis(System.nanoTime()-startDumpHeap);//开始分析内存泄漏文件查找内存泄漏路径

heapdumpListener.analyze(newHeapDump(heapDumpFile,reference.key,reference.name,watchDurationMs,gcDurationMs,

heapDumpDurationMs));

}

}

以上代码部分总结如下:首先创建一个RefWatcher,启动一个ActivityRefWatcher

通过ActivityLifecyclecallback将Activity的onDestroy生命周期给关联起来

最后通过执行execute线程来分析泄漏信息探讨LeakCanary中Activity泄漏检测机制代码

在上面的ensureGone方法中最后我们发现有这样的代码heapdumpListener.analyze(newHeapDump(heapDumpFile,reference.key,reference.name,watchDurationMs,gcDurationMs,

heapDumpDurationMs));

}

通过跟踪发现analyze方法该方法是HeapDump类中的一个interface接口,再查看它的实现类发现在ServiceHeapDumpListener这个类中的方法@Overridepublicvoidanalyze(HeapDumpheapDump){

checkNotNull(heapDump,"heapDump");

HeapAnalyzerService.runAnalysis(context,heapDump,listenerServiceClass);

}

继续跟踪runAnalysis方法发现在HeapAnalyzerService中,且该类继承了intentService,因此它将会每次调用onHandleIntent方法@Override

protectedvoidonHandleIntent(Intentintent){

StringlistenerClassName=intent.getStringExtra(LISTENER_CLASS_EXTRA);

HeapDumpheapDump=(HeapDump)intent.getSerializableExtra(HEAPDUMP_EXTRA);

AnalysisResultresult=heapAnalyzer.checkForLeak(heapDump.heapDumpFile,heapDump.referenceKey);

AbstractAnalysisResultService.sendResultToListener(this,listenerClassName,heapDump,result);

}

通过checkForLeak方法来分析内存泄漏信息的结果,并通过sendResultToListener显示最终的结果publicAnalysisResultcheckForLeak(FileheapDumpFile,StringreferenceKey){longanalysisStartNanoTime=System.nanoTime();if(!heapDumpFile.exists()){

Exceptionexception=newIllegalArgumentException("Filedoesnotexist:"+heapDumpFile);returnfailure(exception,since(analysisStartNanoTime));

}

ISnapshotsnapshot=null;try{

snapshot=openSnapshot(heapDumpFile);//生成内存快照信息

IObjectleakingRef=findLeakingReference(referenceKey,snapshot);//查看内存的引用

//Falsealarm,weakreferencewasclearedinbetweenkeycheckandheapdump.

if(leakingRef==null){returnnoLeak(since(analysisStartNanoTime));

}

StringclassName=leakingRef.getClazz().getName();

AnalysisResultresult=

findLeakTrace(analysisStartNanoTime,snapshot,leakingRef,className,true);if(!result.leakFound){

result=findLeakTrace(analysisStartNanoTime,snapshot,leakingRef,className,false);//寻找内存泄漏的路径

}returnresult;

}catch(SnapshotExceptione){returnfailure(e,since(analysisStartNanoTime));

}finally{

cleanup(heapDumpFile,snapshot);

}

总结checkForLeak方法

1.把.hprof转为 Snapshotsnapshot=openSnapshot(heapDumpFile);

2.找出泄漏的对象/泄漏对象的最短路径IObjectleakingRef=findLeakingReference(referenceKey,snapshot);

AnalysisResultresult=

findLeakTrace(analysisStartNanoTime,snapshot,leakingRef,className,true);

findLeakingReference作用①在snapshot快照中找到第一个弱引用即为内存发生泄漏的引用

②遍历这个对象的所有实例信息

③如果发现存在key值与之前定义封装好的key值相同,那么返回这个定位到的泄漏对象

findLeakTrace是通过获取内存泄漏的引用来获取泄漏路径的最短路径了解LeakCanary的原理Activity Destroy()之后将它放在一个WeakReference中

将WeakReference关联到一个ReferenceQueue

查看ReferenceQueue是否存有Activity的引用

如果该Activity泄露了,Dump出heap信息,然后去分析泄漏路径

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。