“会系统优化吗?你了解怎样定位运行内存难题吗?”招聘者和蔼可亲地坐到小会议厅的一边,亲切问有点儿抑制的小赵。

“那就是...换句话说,应用LeakCanary来检验泄露,随后寻找与泄露相对性应的部位,改正错误的编码,回收利用未循环系统的引入,并提升长生命期进程和短生命期进程范围内的相互依赖”。

"你了解泄露加那利怎样剖析内存泄漏吗?"

“过意不去,平常没留意。”小赵想着:“为何招聘面试的情况下一直问这一?我只是个一般的小白。

app的性能优化一直是开发设计不可或缺的一部分,系统优化是这其中的一个关键。内存泄漏造成的内存溢出和奔溃,运行内存颤动造成的卡涩。的确危害了客户的感受。大家常常应用LeakCanary来精准定位内存泄漏。现在是时候探寻大家怎样完成它了。

专有名词了解

Hprof: hprof文档是一个含有文件格式后缀名的Java运行内存快照更新文档(Heap Profile的简称)。hprof,用以leakCanary WeakReference中的运行内存节约剖析:弱引用,当一个目标只被WeakReference偏向,而沒有别的强引入时,假如这时GC已经运作,那麼这一目标将被回收利用,无论当今运行内存空是不是充足。在leakCanary中用以监管回收利用的没用目标能否被释放出来。窗帘布:Square的另一个开源框架,窗帘布为解决安卓系统对话框给予了一个集中化的运用程序编写插口。用在leakCanary中,监管对话框rootView分离出来后的内存泄漏。

文件目录

文中主要是从以下几个方面开展剖析。

怎样在新项目中应用 LeakCanary专用工具官方网基本原理表明默认设置怎样监视Activity ,view ,fragment 和 viewmodelWatcher.watch(object) 怎样监视内存泄漏如何保存内存泄漏运行内存文档怎样剖析内存泄漏文档展现内存泄漏局部变量到ui中 不兼容在 Docs 外黏贴 block

一,怎么使用?

朋友网文本文档,能够见到使用方法非常简单,基本上使用方法只必须加上有关依靠就可以。

//(1)debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'复制代码

DebugImplementation仅有在编译程序开发者模式并最后装包调节apk时才合理。注(1):标识的编码用一行复位。如何做?网页源代码,我们可以见到leakcanary根据ContentProvider开展复位,并启用真真正正的复位编码AppWatcher。在AppWatcherInstaller类的oncreate方式中手动式安裝(应用软件)。该给予程序流程在AndroidManifest.xml中申请注册,申请注册的ContentProvider将在应用软件运作时全自动回调函数oncreate方式。

internal sealed class AppWatcherInstaller : ContentProvider() { /**[MainProcess] automatically sets up the LeakCanary code that runs in the main app process. */ // (1) internal class MainProcess : AppWatcherInstaller() internal class LeakCanaryProcess : AppWatcherInstaller() override fun onCreate(): Boolean { val application = context!!.applicationContext as Application ///(2) AppWatcher.manualInstall(application) return true } //... }复制代码

表述源码中的数据标识。

编码中界定了2个内部类承继自 AppWatcherInstaller。当客户附加依靠 leakcanary-android-process 控制模块的情况下,全自动在 process=”:leakcanary” 也申请注册该provider。

相关编码,请参照leakcanary-android-process控制模块中的AndroidManifest.xml。

这也是正确的复位编码申请注册通道

二,官方网论述。

官方网叙述。

这一节来自于官方网站的原理,表明了LeakCanary紧密安裝后,会根据四个流程自动识别并汇报内存泄漏:

检验被拥有的目标LeakCanary 挂勾到 Android 生命期以自动识别主题活动和精彩片段什么时候被消毁并应开展废弃物搜集。这种被清理的另一半被传送给一个ObjectWatcher,它拥有对他们的弱引用。 能够积极观查一个不必须的目标例如一个 dettached view 或是 早已消毁的 presenterAppWatcher.objectWatcher.watch(myDetachedView, "View was detached")复制代码

假如ObjectWatcher等待5秒左右并运作废弃物搜集后沒有消除保存的弱引用,则被监控的另一半被觉得是保存的,很有可能会被泄露。LeakCanary将此纪录到Logcat:

D LeakCanary: Watching instance of com.example.leakcanary.MainActivity (Activity received Activity#onDestroy() callback) ... 5 seconds later ...D LeakCanary: Scheduling check for retained objects because found new object retained复制代码Dumping the heap 数据归档堆信息内容到文档中当保存目标的总数做到阀值时,LeakCanary 将 Java 运行内存快照更新 dumping 数据归档到 Android 系统文件上的.hprof文档(堆内存快照更新)中。数据归档堆会在短期内冻洁应用软件,并展现下面的图的土司面包:剖析堆内存LeakCanary应用Shark分析.hprof文档并在该运行内存快照更新文档中精准定位被保存的泄露目标。 针对每一个保存目标,LeakCanary 寻找该目标的引入途径,该引入阻拦了废弃物回收器对它的回收利用。也就是泄露追踪。 LeakCanary为每一个泄露追踪建立一个签字 (对拥有的引入特性开展求和做sha1Hash),并将具备同样签字的泄露(即由同样不正确造成的泄露)组成在一起。怎样建立签字和根据签字排序尚需后文剖析。归类内存泄漏LeakCanary 将它在您的使用中看到的泄露分成两大类:Application Leaks (应用软件泄露)和Library Leaks(库泄露)。一个Library Leaks是由已经知道的第三方库造成的,你没有决策权。这类泄露已经危害您的应用软件,但悲剧的是,修补它很有可能没有您的调节范畴内,因而 LeakCanary 将其提取出来。 这两个类型分离在Logcat結果中打印出:====================================HEAP ANALYSIS RESULT====================================0 APPLICATION LEAKS====================================1 LIBRARY LEAK...┬───│ GC Root: Local variable in native code│...复制代码

LeakCanary在其泄露目录展现时会将其用Library Leak 标识标识:showtoast图标样式-toast的show方法-第1张图片泄露金丝雀将在它的泄露目录展现选用库泄露标识来标识它:

img

LeakCanary附加了一个已经知道泄露的数据库查询,根据引入名字的匹配算法来鉴别。比如:

Leak pattern: instance field android.app.Activity$1#this$0Description: Android Q added a new IRequestFinishCallback$Stub class [...]┬───│ GC Root: Global variable in native code│├─ android.app.Activity$1 instance│ Leaking: UNKNOWN│ Anonymous subclass of android.app.IRequestFinishCallback$Stub│ ↓ Activity$1.this$0│ ~~~~~~╰→ com.example.MainActivity instance复制代码

公共图书馆泄露一般我们无法调节和修补它。您能够在AndroidReferenceMatchers类中查验已经知道泄露的详细目录。假如看到有无法识别的安卓系统SDK泄漏,请检举。您可以自定已经知道库泄露的目录。

第三,监管主题活动,精彩片段,rootView和viewmodel。

前边看到的复位编码如下所示,使我们看一下manualInstall的內部关键点。

///复位编码AppWatcher.manualInstall(application)///AppWatcher 的 manualInstall 编码@JvmOverloadsfun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), watchersToInstall: List = appDefaultWatchers(application)) { //*******查验是不是为主导进程********/ checkMainThread() if (isInstalled) { throw IllegalStateException( "AppWatcher already installed, see exception cause for prior install call", installCause ) } check(retainedDelayMillis >= 0) { "retainedDelayMillis $retainedDelayMillis must be at least 0 ms" } installCause = RuntimeException("manualInstall() first called here") this.retainedDelayMillis = retainedDelayMillis if (application.isDebuggableBuild) { LogcatSharkLog.install() } // Requires AppWatcher.objectWatcher to be set ///(2) LeakCanaryDelegate.loadLeakCanary(application) ///(1) watchersToInstall.forEach { it.install() }}复制代码

AppWatcher做为安卓应用软件的api核心,应用的是ObjectWatcher包。自动安装配备默认设置监视。以上编码的关键环节用数字标明。

(1)安裝默认设置监管观查。

(1)处的程序执行了InstallableWatcher的安装简单,在启用时沒有传送watchersToInstall主要参数,因而采用了appDefaultWatchers(应用软件)。下边的编码为默认设置监视给予了四个Watcher。

fun appDefaultWatchers( application: Application, ///(1.1) reachabilityWatcher: ReachabilityWatcher = objectWatcher): List { return listOf( ///(1.2) ActivityWatcher(application, reachabilityWatcher), ///(1.3) FragmentAndViewModelWatcher(application, reachabilityWatcher), ///(1.4) RootViewWatcher(reachabilityWatcher), ///(1.5) ServiceWatcher(reachabilityWatcher) )}复制代码

大家逐一剖析这四个数据。

(1.1)普适性观察器主要参数

标识为(1.1)的编码是一个ReachabilityWatcher主要参数,它将在建立下面的四个案例时应用。从编码中还可以看得出,reachabilityWatcher案例是AppWatcher: objectWatcher的成员函数,相匹配的创建对象编码如下所示。

/** * The [ObjectWatcher] used by AppWatcher to detect retained objects. * Only set when [isInstalled] is true. */val objectWatcher = ObjectWatcher( clock = { SystemClock.uptimeMillis() }, checkRetainedExecutor = { check(isInstalled) { "AppWatcher not installed" } mainHandler.postDelayed(it, retainedDelayMillis) }, isEnabled = { true })复制代码

能够见到objectWatcher是一个ObjectWatcher目标,承担检验被拥有目标的泄漏状况,第三节会开展剖析。返回ActivityWatcher案例的建立,再次往下查询标识的编码。

(1.2)activity watcher案例进行对主题活动案例的监管。

返回以前,标识为(1.2)的编码建立了一个ActivityWatcher案例,并在组装时安裝它。查验ActivityWatcher类的源码,看一下怎样监管主题活动泄露。

class ActivityWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher { private val lifecycleCallbacks = //(1.2.1) 根据动态代理,结构出生命期回调函数的完成类 object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { //(1.2.3) reachabilityWatcher.expectWeaklyReachable( activity, "${activity::class.java.name} received Activity#onDestroy() callback" ) } } override fun install() { //(1.2.3) application.registerActivityLifecycleCallbacks(lifecycleCallbacks) } override fun uninstall() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) }复制代码

(1.2.1)生命期回调函数案例。

(1.2.1)中的编码建立一个ActivityLifecycleCallbacks的案例,该案例完成application . activitylifecyclebacks..根据“* noOpDelegate *”(),运用动态代理完成了别的回调函数方式。有兴趣的客户能够查询noopdelegate的源码。

(1.2.2)主题活动窃听器的安装方法。

标识为(1.2.2)的编码是复位的主要是编码。这一方式非常简单,便是在应用软件中申请注册lifecycleCallbacks,当主题活动被毁坏时,就转至方式实现完成。

(1.2.3)监管主题活动的onActivityDestroyed回调函数。

标识为(1.2.3)的编码是复位的主要是编码。当主题活动被毁坏时,回调函数这一方式,查验案例是不是泄露,并启用appwatcher。目标观测者。可预估的方式,在这个方式中,主题活动泄露检测得到进行。这时候,我重回了1.1中提及的ObjectWatcher的源码。相关性分析见第4节。

(1.2-end)主题活动监管汇总。

那样就完成了ActivityInstaller,了解了Activity的复位编码和加上摄像头的关键点。汇总为下列流程:

启用ActivityInstaller.install 复位方式在Application 申请注册ActivityLifecycleCallbacks在全部activity onDestroy的过程中启用ObjectWatcher的 expectWeaklyReachable方式,查验过五秒后activity目标是不是有被运行内存回收利用。标识内存泄漏。下一节剖析。检验到内存泄漏的事后实际操作。后文剖析。

(1.3)精彩片段和主视图实体模型观察器监控精彩片段和主视图实体模型案例。

(1.3)建立了一个FragmentAndViewModelWatcher案例。监管精彩片段和主视图实体模型的内存泄漏。

这一类完成了SupportFragment,androidxFragment和androidO的兼容。做为一个sdk开发者,这类兼容模式是能够学习培训的。

private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { for (watcher in fragmentDestroyWatchers) { watcher(activity) } } }override fun install() { application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}复制代码

与ActivityWatcher同样,安裝存储器生命期监管。可是,在建立每一个主题活动时,它会交到fragmentDestroyWatchers元素开展监管。因而,fragmentDestroyWatchers是精彩片段和主视图实体模型的真真正正侦听器。下面,看一下fragmentDestroyWatchers元素的建立:

private val fragmentDestroyWatchers: List Unit> = run { val fragmentDestroyWatchers = mutableListOf Unit>() //(1.3.1) android架构内置的fragment泄露检测适用从 AndroidO(26)逐渐。 if (SDK_INT >= O) { fragmentDestroyWatchers.add( AndroidOFragmentDestroyWatcher(reachabilityWatcher) ) } //(1.3.2) getWatcherIfAvailable( ANDROIDX_FRAGMENT_CLASS_NAME, ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } //(1.3.3) getWatcherIfAvailable( ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } fragmentDestroyWatchers}复制代码

您能见到AndroidOFragmentDestroyWatcher是在內部建立的,用以监管精彩片段。标准是申请注册FragmentManager。fragmentlifecyclecallbacks在fragmentmanager中监管fragment.fragment.view和viewmodel的案例泄露。依据公司文档,安卓系统內部的精彩片段仅仅在Api 26中加上的。因而,LeakCanary自身对android架构的精彩片段泄露监管适用也是以AndroidO(26)逐渐的,参照Code (1.3.1)。在注解1.3.1.1.3.2和1.3.3中创建对象的三个watcher是androidfragmentdestroywatcher.androidxfragmentdestroywatcher和androidsupportfragmentdestroywatcher。內部完成编码类似。根据体现和创建对象不一样的Watcher,完成了androidX.support和Android版本号的兼容。

(1 . 3 . 1)androidfragmentdestroywatcher实例。

(1.3.1)中的编码加上了一个androidO的观测者案例。实际见编码,由于完成相近,剖析参照1.3.2。

(1 . 3 . 2)androidxfragmentdestroywatcher案例。

(1.3.2)处的编码启用getWatcherIfAvailable通过反射面建立AndroidXFragmentDestroyWatcher案例,假如Androidx库不会有,则回到null。如今自动跳转到AndroidXFragmentDestroyWatcher的源码剖析。

internal class AndroidXFragmentDestroyWatcher( private val reachabilityWatcher: ReachabilityWatcher) : (Activity) -> Unit { private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentCreated( fm: FragmentManager, fragment: Fragment, savedInstanceState: Bundle? ) { //(1.3.2.1)复位 ViewModelClearedWatcher ViewModelClearedWatcher.install(fragment, reachabilityWatcher) } override fun onFragmentViewDestroyed( fm: FragmentManager, fragment: Fragment ) { //检测 fragment.view 的泄露状况 val view = fragment.view if (view != null) { reachabilityWatcher.expectWeaklyReachable( view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " "(references to its views should be cleared to prevent leaks)" ) } } override fun onFragmentDestroyed( fm: FragmentManager, fragment: Fragment ) { //检测 fragment 的泄露状况 reachabilityWatcher.expectWeaklyReachable( fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback" ) } } ///复位,申请注册fragmentLifecycleCallbacks override fun invoke(activity: Activity) { if (activity is FragmentActivity) { val supportFragmentManager = activity.supportFragmentManager supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) //申请注册activity的 viewModel 监视回调函数 ViewModelClearedWatcher.install(activity, reachabilityWatcher) } }}复制代码

从源码中还可以见到,复位观察器是根据下列流程开展的。

FragmentManager.registerFragmentLifecycleCallbacks 申请注册监视回调函数ViewModelClearedWatcher.install 复位了针对activity.viewModel的监视在回调函数onFragmentCreated 中回调函数中应用ViewModelClearedWatcher.install申请注册了针对fragment.viewModel的监视。在 onFragmentViewDestroyed 监视 fragment.view 的泄露在 onFragmentDestroyed 监视 fragment的泄露。 监视方式和ActivityWatcher大同小异,不一样是多了个 ViewModelClearedWatcher.install 。如今剖析这一块的源代码,也就是标明中的 (1.3.2.1)。//该watcher 承继了ViewModel,生命期被 ViewModelStoreOwner 管理方法。internal class ViewModelClearedWatcher( storeOwner: ViewModelStoreOwner, private val reachabilityWatcher: ReachabilityWatcher) : ViewModel() { private val viewModelMap: Map? init { //(1.3.2.3)通过反射面获得任何的 store 储存的全部viewModelMap viewModelMap = try { val mMapField = ViewModelStore::class.java.getDeclaredField("mMap") mMapField.isAccessible = true @Suppress("UNCHECKED_CAST") mMapField[storeOwner.viewModelStore] as Map } catch (ignored: Exception) { null } } override fun onCleared() { ///(1.3.2.4) viewmodle 被清除释放出来的情况下回调函数,查验全部viewmodle 是不是会出现泄露 viewModelMap?.values?.forEach { viewModel -> reachabilityWatcher.expectWeaklyReachable( viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback" ) } } companion object { fun install( storeOwner: ViewModelStoreOwner, reachabilityWatcher: ReachabilityWatcher ) { val provider = ViewModelProvider(storeOwner, object : Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T = ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T }) ///(1.3.2.2) 获得ViewModelClearedWatcher案例 provider.get(ViewModelClearedWatcher::class.java) } }}复制代码

根据编码,我们可以见到主视图实体模型的泄露监管是根据建立一个新的主视图实体模型案例来完成的。在这个案例的onCleared,监视storeOwner的别的主视图实体模型的泄露。标识的编码被逐一剖析:

(1.3.2.2)编码:

获得ViewModelClearedWatcher案例,并在自定加工厂中传送storeOwner和reachabilityWatcher。

(1.3.2.3)编码:

根据反射面获得储存使用者的主视图模型图。

(1.3.2.4)编码:

当ViewModel进行其OnClear每日任务时,它逐渐监管storeOwner有着的全部view model的内存泄漏。

(1.3-end)按精彩片段和主视图实体模型的泄露监管引言:

监管方式基本都是根据ObjectWatcher的expectWeaklyReachable可以达到方式来完成的。精彩片段应用FragmentLifecyclerCallback开展申请注册,而viewModel则在相应的StoreOwner下建立一个监管ViewModel,完成生命期的回应。大家还能够学习培训怎样根据反射面建立相对应的服务平台兼容的完成目标。及其根据创建视图实体模型来监管主视图实体模型生命期的念头。

(1.4)rootview switch的源码剖析。

默认设置状况下,在四个Watcher中,下一个是RootViewWatcher。Windowview监管取决于squre自身的窗帘布架构。

implementation "com.squareup.curtains:curtains:1.0.1"复制代码

此类的重要源码如下所示:

private val listener = OnRootViewAddedListener { rootView -> //如果是 Dialog TOOLTIP, TOAST, UNKNOWN 等种类的Windows //trackDetached 为true if (trackDetached) { rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { val watchDetachedView = Runnable { reachabilityWatcher.expectWeaklyReachable( rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback" ) } override fun onViewAttachedToWindow(v: View) { mainHandler.removeCallbacks(watchDetachedView) } override fun onViewDetachedFromWindow(v: View) { mainHandler.post(watchDetachedView) } }) } } override fun install() { Curtains.onRootViewsChangedListeners = listener } override fun uninstall() { Curtains.onRootViewsChangedListeners -= listener }}复制代码

参照重要编码,便是在窗帘布中加上一个onRootViewsChangedListeners窃听器。当对话框种类为* *提示框* * * * *专用工具提醒* * *,* * *土司面包* * *,或不明时,请在onViewDetachedFromWindow中监听泄露。当对话框根主视图更改时,窗帘布中的窃听器将被全局性启用。窗帘布是squareup的另一个开源系统库,窗帘布为解决安卓系统对话框给予了一个集中化的运用程序编写插口。搬至他的官方网库房。

(1.5)服务项目监控器监控服务项目内存泄漏。

随后是AppWatcher中的最后一个Watcher。服务项目观测者.代码比较长,关键了解被提取。

(1.5.1)最先看一下成员函数activityThreadServices:

private val servicesToBeDestroyed = WeakHashMap>()private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }private val activityThreadInstance by lazy { activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!}private val activityThreadServices by lazy { val mServicesField = activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true } @Suppress("UNCHECKED_CAST") mServicesField[activityThreadInstance] as Map}复制代码

训练阅读文章服务项目是一个包括全部匹配的地形图。您能够在编码中见到,mServices自变量是根据反射面立即从ActivityThread案例中得到的。给activityThreadServices一个值。源码中几个互换实际操作,在组装时实行。关键目标是在最开始与服务项目有关的生命期回调函数中加上一些勾子,这种勾子用以监管内存泄漏,并将在删除时被更换。

(1 . 5 . 2)swapactivity threadhandlercallback:

获得ActivityThread的程序处理,并且用加上的程序处理更换其回调函数的handleMessage。回拔收费标准编码如下所示。

Handler.Callback { msg -> if (msg.what == STOP_SERVICE) { val key = msg.obj as IBinder activityThreadServices[key]?.let { onServicePreDestroy(key, it) } } mCallback?.handleMessage(msg) ?: false}复制代码

从编码中还可以见到,STOP_SERVICE的实际操作有一个勾子,以前实行过onServicePreDestroy。关键作用是为服务项目建立一个弱引用,并将其加上到service sto destroled[token]中。

(1.5.3)随后看看swapActivityManager方法。

该方式完成了用主题活动管理工具的动态代理类更换主题活动管理工具。编码如下所示:

Proxy.newProxyInstance( activityManagerInterface.classLoader, arrayOf(activityManagerInterface)) { _, method, args ->//private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting" if (METHOD_SERVICE_DONE_EXECUTING == method.name) { val token = args!![0] as IBinder if (servicesToBeDestroyed.containsKey(token)) { ///(1.5.3) onServiceDestroyed(token) } } try { if (args == null) { method.invoke(activityManagerInstance) } else { method.invoke(activityManagerInstance, *args) } } catch (invocationException: InvocationTargetException) { throw invocationException.targetException }}复制代码

编码表明,被编辑的ActivityManager在启用serviceDoneExecuting方式时加上了一个勾子,假如服务项目在以前加上的servicestodestroyedmap中,则启用onServiceDestroyed来监控服务的内存泄漏。

(1 . 5 . 4)OnServiceDressed编码如下所示。

private fun onServiceDestroyed(token: IBinder) { servicesToBeDestroyed.remove(token)?.also { serviceWeakReference -> serviceWeakReference.get()?.let { service -> reachabilityWatcher.expectWeaklyReachable( service, "${service::class.java.name} received Service#onDestroy() callback" ) } }}复制代码

里边的编码很了解,和以前监管主题活动一样。返回互换主题活动管理工具方式,看一下代理商主题活动管理工具的实际种类。您能见到代理商目标表明在下面的编码中,依据不一样的版本号,它可能是ActivityManager案例,也可能是activity manager案例。代理商插口是class . for name(“Android . app . iactivitymanager”)。

val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { "android.app.ActivityManager" to "IActivityManagerSingleton"} else { "android.app.ActivityManagerNative" to "gDefault"}复制代码

(1.5-end)服务项目泄露检测汇总。

总得来说,服务项目的泄露剖析根据加上勾子来监控一些系统软件实行。关键分成下列流程:

获得ActivityThread中mService变量,获得service案例的引入根据swapActivityThreadHandlerCallback 在ActivityThread 的 Handler.sendMessage 中加上勾子,在实行到msg.what == STOP_SERVICE 的情况下

第四,定期检查剖析ObjectWatcher保存的目标。

使我们转到目标观测者的期待可以达到方式。

@Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String) { //是不是开启 , AppWatcher 拥有的ObjectWatcher 默认设置是运用的 if (!isEnabled()) { return } ///清除以前早已被回收利用的监视目标 removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() //(1) 建立弱引用 val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { "Watching " (if (watchedObject is Class) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") (if (description.isNotEmpty()) " ($description)" else ") " with key $key" } watchedObjects[key] = reference checkRetainedExecutor.execute { //(2) moveToRetained(key) }}复制代码

再次剖析源码中标识的地区。

(1)建立弱引用。

标识的编码(1.2.4)是复位的主要是编码,建立待观查目标的WeakReference,gc后做为目标信息内容储存序列传送到序列。在WeakReference中,当目标被gc拥有时,被包裝的目标将被压进序列。序列能够稍候观查。

(2) moveToRetained(键),查验相匹配键目标的预埋。

做为Executor的运作者,在AppWatcher中,默认设置延迟时间是在实行一个方式网页源代码剖析以前的五秒左右。

@Synchronized private fun moveToRetained(key: String) {///清除早已被回收利用的观查目标 removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { //纪录泄露時间 retainedRef.retainedUptimeMillis = clock.uptimeMillis() //回调函数泄露监视 onObjectRetainedListeners.forEach { it.onObjectRetained() } }}复制代码

从里面的编码能够看得出,ObjectWatcher有下列流程来监管内存泄漏。

消除早已被运行内存回收利用的监视目标建立弱引用,传到 ReferenceQueue 做为gc 信息内容储存序列在延迟时间特定的时间段后,再度查验对于的目标能否被回收利用(根据查验ReferenceQueue序列内有没有该WeakReference案例)检验到目标沒有被回收利用后,回调函数 onObjectRetainedListeners 们的 onObjectRetained

五,dumpHeap,哪些DumpHeap步骤?

(1.1)objectWatcher加上了一个OnObjectRetainedListeners来监视。

回到起点的应用软件监控器指南安装方法。您能见到loadLeakCanary方式强制执行。编码如下所示:

///(2) LeakCanaryDelegate.loadLeakCanary(application) //反射面获得InternalLeakCanary案例 val loadLeakCanary by lazy { try { val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary") leakCanaryListener.getDeclaredField("INSTANCE") .get(null) as (Application) -> Unit } catch (ignored: Throwable) { NoLeakCanary }}复制代码

方式根据反射面得到InternalLeakCanary的静态数据案例。并启用了他的invoke(application: Application)方式,下面使我们看一下InternalLeakCanary的方式:

override fun invoke(application: Application) { _application = application checkRunningInDebuggableBuild() //(1.2)加上 addOnObjectRetainedListener AppWatcher.objectWatcher.addOnObjectRetainedListener(this) val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application)) //Gc触发器原理 val gcTrigger = GcTrigger.Default val configProvider = { LeakCanary.config } val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME) handlerThread.start() val backgroundHandler = Handler(handlerThread.looper)///(1.3) heapDumpTrigger = HeapDumpTrigger( application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper, configProvider ) ///(1.4) 加上application前后左右台转变监视 application.registerVisibilityListener { applicationVisible -> this.applicationVisible = applicationVisible heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible) } //(1.5) registerResumedActivityListener(application) //(1.6) addDynamicShortcut(application) // 6 分辨是不是应当DumpHeap // We post so that the log happens after Application.onCreate() mainHandler.post { // https://github.com/square/leakcanary/issues/1981 // We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref // which blocks until loaded and that creates a StrictMode violation. backgroundHandler.post { SharkLog.d { when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) { is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text) is Nope -> application.getString( R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason() ) } } } }}复制代码

在我们见到复位时,大家干了这六个流程。

(1.2) 将自身添加到ObjectWatcher 的目标出现异常拥有窃听器中(1.3)建立运行内存快照更新数据归档触发器原理 HeapDumpTrigger(1.4)监视application 前后左右台变化,而且纪录赶到后台管理時间,有利于LeakCanary 对于刚进入后台管理的一些destroy实际操作做泄露检测(1.5)申请注册activity生命期回调函数,获得当今resumed的activity案例(1.6)加上信息的桌面上快捷入口(1.7)在多线程进程中,分辨是不是处在可dumpHeap的情况,假如处在开启一次内存泄漏查验 在其中最重要的是 1.2,大家关键剖析做为ObjectRetainedListener 他在回调函数中干了什么工作中。

(1.2)提升出现异常物件维持监管。

您能见到编码(1.2),它将自身加上到objectWatcher中的泄露监管回调函数中。当ObjectWatcher检验到该目标依然被出现异常拥有时,它将回调函数onObjectRetained方式。依据源码,启用heapDumpTrigger的scheduleRetainedObjectCheck方式,编码如下所示。

fun scheduleRetainedObjectCheck() { if (this::heapDumpTrigger.isInitialized) { heapDumpTrigger.scheduleRetainedObjectCheck() }}复制代码

HeapDumpTrigger,说白了,便是运行内存快照更新数据归档的触发器原理。在回调函数中,最终启用了HeapDumpTrigger的checkRetainedObjects方式来查验内存泄漏。

(1.3)查验内存泄漏查验保存目标。

private fun checkRetainedObjects() { val iCanHasHeap = HeapDumpControl.iCanHasHeap() val config = configProvider() //省去一些编码,主要是分辨 iCanHasHeap。 //假如当今处在不dump运行内存快照更新的情况,就先不解决。如果有新的出现异常拥有目标被发觉则推送通告提醒 //%d retained objects, tap to dump heap /** ...*/ var retainedReferenceCount = objectWatcher.retainedObjectCount //积极开启gc if (retainedReferenceCount > 0) { gcTrigger.runGc() //再次获得出现异常拥有目标 retainedReferenceCount = objectWatcher.retainedObjectCount } //假如泄露总数低于阀值,且app在前台接待,或是刚转到后台管理,就展现泄露通告,并先回到 if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return//假如泄露总数抵达dumpHeap规定,再次向下 ///数据归档运行内存快照更新在 WAIT_BETWEEN_HEAP_DUMPS_MILLIS (默认设置60秒)只能开启一次,假如以前刚开启过,就先不转化成运行内存快照更新,立即推送通告了事。//省去数据归档快照更新机会分辨,不符合得话会提醒 Last heap dump was less than a minute ago/**...*/ dismissRetainedCountNotification() val visibility = if (applicationVisible) "visible" else "not visible" ///数据归档运行内存快照更新 dumpHeap( retainedReferenceCount = retainedReferenceCount, retry = true, reason = "$retainedReferenceCount retained objects, app is $visibility" )}复制代码

从这一块中还能够看得出,检验是不是必须dumpHeap分成四个流程。

要是没有检查到出现异常拥有的目标,回到如果有出现异常目标,积极开启gc假如也有出现异常目标,便是内存泄漏了。分辨泄露总数是不是抵达必须dump的程度分辨一分钟内是不是叫开展过dump了dumpHeap 前边全是分辨编码,重要关键取决于dumpHeap方式

(1.4)dumpHeap数据归档运行内存快照更新。

private fun dumpHeap( retainedReferenceCount: Int, retry: Boolean, reason: String) { saveResourceIdNamesToMemory() val heapDumpUptimeMillis = SystemClock.uptimeMillis() KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis when (val heapDumpResult = heapDumper.dumpHeap()) { is NoHeapDump -> { //省去 dump不成功,等候再试编码和推送不成功通告编码 } is HeapDump -> { lastDisplayedRetainedObjectCount = 0 lastHeapDumpUptimeMillis = SystemClock.uptimeMillis() ///消除 objectWatcher 中,在heapDumpUptimeMillis以前拥有的目标,也就是早已dump的目标 objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis) // 推送文档到HeapAnalyzerService分析 HeapAnalyzerService.runAnalysis( context = application, heapDumpFile = heapDumpResult.file, heapDumpDurationMillis = heapDumpResult.durationMillis, heapDumpReason = reason ) } }}复制代码

在HeapDumpTrigger#dumpHeap中启用了AndroidHeapDumper#dumpHeap方式。数据归档后,马上拨通heapanalyzerservice。运行内存剖析的runanalysis。下一节将剖析这类方式。先看一下AndroidHeapDumper#dumHeap源码。

override fun dumpHeap(): DumpHeapResult {//建立新的hprof 文档 val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return NoHeapDump val waitingForToast = FutureResult() ///展现dump土司面包 showToast(waitingForToast) ///假如展现土司面包時间超出五秒,也不dump了 if (!waitingForToast.wait(5, SECONDS)) { SharkLog.d { "Did not dump heap, too much time waiting for Toast." } return NoHeapDump } //省去dumpHeap状态栏提醒信息编码 val toast = waitingForToast.get() return try { val durationMillis = measureDurationMillis { //启用DumpHprofData Debug.dumpHprofData(heapDumpFile.absolutePath) } if (heapDumpFile.length() == 0L) { SharkLog.d { "Dumped heap file is 0 byte length" } NoHeapDump } else { HeapDump(file = heapDumpFile, durationMillis = durationMillis) } } catch (e: Exception) { SharkLog.d(e) { "Could not dump heap" } // Abort heap dump NoHeapDump } finally { cancelToast(toast) notificationManager.cancel(R.id.leak_canary_notification_dumping_heap) }}复制代码

在这个方式中,最终启用Debug.dumpHprofData方式来进行hprof快照更新的转化成。

第六,剖析记忆力身心健康剖析服务项目。

从里面的编码剖析能够看得出,dumpHeap后边是运行运行内存剖析服务项目的方式。如今大家跳至HeapAnalyzerService的源码。

override fun onHandleIntentInForeground(intent: Intent?) { //省去主要参数获得编码 val config = LeakCanary.config val heapAnalysis = if (heapDumpFile.exists()) { analyzeHeap(heapDumpFile, config) } else { missingFileFailure(heapDumpFile) } //省去健全剖析結果特性的编码 onAnalysisProgress(REPORTING_HEAP_ANALYSIS) config.onHeapAnalyzedListener.onHeapAnalyzed(fullHeapAnalysis)}复制代码

能够见到关键是在analyzeHeap上,在其中heapanalyzer被称作# analyzeheapanalyzer类坐落于shark控制模块中。

(1)身心健康剖析#剖析

运行内存统计分析方法编码如下所示:

fun analyze( heapDumpFile: File, leakingObjectFinder: LeakingObjectFinder, referenceMatchers: List = emptyList(), computeRetainedHeapSize: Boolean = false, objectInspectors: List = emptyList(), metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP, proguardMapping: ProguardMapping? = null): HeapAnalysis { //省去运行内存快照更新文档无法访问的解决编码 return try { listener.onAnalysisProgress(PARSING_HEAP_DUMP) ///io载入 运行内存快照更新 val sourceProvider = ConstantMemoryMetricsDualSourceProvider(FileSourceProvider(heapDumpFile)) sourceProvider.openHeapGraph(proguardMapping).use { graph -> val helpers = FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors) //重要编码:在这里寻找泄露的結果及其其相匹配启用栈 val result = helpers.analyzeGraph( metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime ) val lruCacheStats = (graph as HprofHeapGraph).lruCacheStats() ///io载入情况 val randomAccessStats = "RandomAccess[" "bytes=${sourceProvider.randomAccessByteReads}," "reads=${sourceProvider.randomAccessReadCount}," "travel=${sourceProvider.randomAccessByteTravel}," "range=${sourceProvider.byteTravelRange}," "size=${heapDumpFile.length()}" "]" val stats = "$lruCacheStats $randomAccessStats" result.copy(metadata = result.metadata ("Stats" to stats)) } } catch (exception: Throwable) { //省去错误处理 }}复制代码

根据剖析编码,能够了解剖析运行内存快照更新分成下列五个流程:

载入hprof运行内存快照更新文档寻找LeakCanary 标识的泄露目标们的数目和弱引用包裝 ids,class name 为com.squareup.leakcanary.KeyedWeakReference

keyedwaekreferencefinder # findrakingobjectid中的编码。

寻找泄露目标的gcRoot逐渐的途径

pathfinder # findpathsfromgcroots中的编码。

回到剖析結果,走結果回调函数回调函数内 展现运行内存剖析取得成功或是失效的状态栏信息,并将泄露目录储存到数据库查询中

相关详细资料,请参照DefaultOnHaepanalylistener # OnHaepanalyzed和LeaksDbHelper。

点开状态栏自动跳转到LeaksActivity 展现内存泄漏信息内容。

七.汇总。

最终,至始至终,我终于整理了一波LeakCanary的源码。

我还在这一全过程初中到许多-->

积极启用Gc的方法 GcTrigger.Default.runGc()Runtime.getRuntime().gc()复制代码seald class 密封性类来表述情况,例如下列好多个(重要益处就是应用when能够立即遮盖全部状况,而无须应用else)。sealed class ICanHazHeap { object Yup : ICanHazHeap() abstract class Nope(val reason: () -> String) : ICanHazHeap() class SilentNope(reason: () -> String) : Nope(reason) class NotifyingNope(reason: () -> String) : Nope(reason)}sealed class Result { data class Done( val analysis: HeapAnalysis, val stripHeapDumpDurationMillis: Long? = null ) : Result() data class Canceled(val cancelReason: String) : Result()}复制代码了解了系统软件建立运行内存快照更新的api Debug.dumpHprofData(heapDumpFile.absolutePath)复制代码知道根据 ReferenceQueue 检测内存目标能否被gc,以前WeakReference都非常少用。学了leakCanary的分控制模块观念。做为sdk,许多程序模块引进全自动打开。例如 leakcanary-android-process 全自动打开相匹配过程等。学了根据反射面hook编码,更换案例达到加上勾子的实际操作。例如在Service泄露监视编码中,更换Handler和activityManager的实际操作。

多看看源码就行。怪不得我之前找工作难。看得太少。

评论(0条)

刀客源码 游客评论