使用MAT来分析内存问题,有一些门槛,会有一些难度,并且效率也不是很高,对于一个内存泄漏问题,可能要进行多次排查和对比才能找到问题原因。 为了能够简单迅速的发现内存泄漏,Square公司基于MAT开源了LeakCanary 总结来说LeakCanary是一个基于MAT用来检测内存泄漏的一个有效的简单好用的工具。 申请大容量内存导致的OOM问题、Bitmap内存未释放问题,Service 中的内存泄漏无法检测等,需要我们用Mat。 暂时不展开以后再去做这块的介绍其实总结出来都大同小异 SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,比如说:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用放到与之关联的ReferenceQueue中,所以我们如果在对象该回收的时候去检测队列如果发现队列中有这个对象的若应用说明这个对象被正常回收了,如果检测是发现没有对象的弱引用不在说明对象没有被正常回收就是发生了内存泄漏。但是实际操作中在我们认为对象该被回收的时候实际上可能有一定的延迟或者并没有发生GC,所以需要我们给他一定的回收时间和重试时间或者手动触发一下GC(不一定执行)。 2.0之后只需要在gradle里添加一个这个就可以了 2.4都是用kotlin写的。 注册install的工作都放到AppWatcherInstaller中了。 首先我们要先了解LeakCanary-Android主要是检测Activity和Fragment的内存泄漏。其他的对象的泄漏需要我们自己去 跟踪AppWatcherInstaller中注册的代码到AppWatcher 继续追踪代码 InternalAppWatcher.install(application)到InternalAppWatcher InternalAppWatcher中做了ActivityDestroyWatcher.install Fragment 则要复杂一些FragmentDestroyWatcher中的install中在Activity启动的onCreate监听中通过Activity获取FragmentManager,通过FragmentManager.registerFragmentLifecycleCallbacks注册监听,主要是在onFragmentViewDestroyed 和 onFragmentDestroyed方法中监听 ,onFragmentViewDestroyed中主要是对fragment.view的泄漏检测。 其中对AndroidO 以及 AndroidX和Android库进行了适配: 通过上面的分析我们发现Fragment和Activity都是通过监听destroy方法然后,去执行ObjectWatcher的watch方法,那看来核心的检测代码就在里边了。那我们现在就去探索一下吧。 追踪objectWatcher.watch方法。 也就是说当一个对象被强引用和弱引用同时引用的时候,GC时是不会被回收的,只有在生命周期结束之后才会被GC判定为只存在弱引用而最终被回收掉。 checkRetainedExecutor和对Activity,Fragment的检测ObjectWatch 接着代码中分析的最后一步(有【标记最后一步】标记的地方 handler的五秒信息后清除后引用对象不为null), 鼠标放到这个方法上边 Command + 触摸板点击,我们会发现我们到了InternalLeakCanary的invoke方法中但是这个地方我们从前边分析过来也没发现那里调用了 但是不要急他肯定在哪里被调用了,我们看的时候都注意在主流程上边,忽略了这个没关系我们再回头去看下到底哪里使用了它,经过在前边AppWatcherInstaller,AppWatcher,InternalAppWatcher Kotlin中init代码块和构造方法以及伴生对象中代码的调用时机及执行顺序 继续跟踪 分析scheduleRetainedObjectCheck: 然后在分析checkRetainedObjects: 总结一下就是: 6、如果上面的条件都符合了,就可以开始进行堆栈的分析了 好了 到这里LeakCanary的原理和源码分析完毕
LeakCanary原理分析
简介
不足
常见的内存泄漏
常见的内存泄漏和解决方式1
常见的内存泄漏和解决方式2核心原理
使用
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
AppWatcherInstaller这个类它继承自ContentProvider,四大组件之一。ContentProvider的特点就是不用显示调用初始化,在执行完application的初始化后就会调用ContentProvider的onCreate()方法。正是利用这一点,LeakCanary把注册写在了这里面,有系统自动调用完成,对开发者完全无感知。
这里也可以看出我们了解四大组件源码Android启动流程的重要性。我们可以做很多优化的事情。internal sealed class AppWatcherInstaller : ContentProvider() { ....省略代码.... override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) return true } ....省略代码.... }
原理
何时检测泄漏
我们带着问题先去看下他的注册源码。 AppWatcher.manualInstall(application)
object AppWatcher { .....省略代码 /** * [AppWatcher] is automatically installed in the main process on startup. You can * disable this behavior by overriding the `leak_canary_watcher_auto_install` boolean resource: * * ``` * <?xml version="1.0" encoding="utf-8"?> * <resources> * <bool name="leak_canary_watcher_auto_install">false</bool> * </resources> * ``` * * If you disabled automatic install then you can call this method to install [AppWatcher]. */ fun manualInstall(application: Application) { InternalAppWatcher.install(application) } }
/** * Note: this object must load fine in a JUnit environment */ internal object InternalAppWatcher { ...省略代吗 fun install(application: Application) { ...省略代吗 ActivityDestroyWatcher.install(application, objectWatcher, configProvider) FragmentDestroyWatcher.install(application, objectWatcher, configProvider) ...省略代吗 } ...省略代吗 }
FragmentDestroyWatcher.install
我们继续追踪到FragmentDestroyWatcher和ActivityDestroyWatcher的代码细节。internal class ActivityDestroyWatcher private constructor( private val objectWatcher: ObjectWatcher, private val configProvider: () -> Config ) { //创建Activity生命周期监听对象 监听Activity destroy方法然后调用 objectWatcher private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { //判断config中配置是否监听activity泄漏 if (configProvider().watchActivities) { objectWatcher.watch( activity, "${activity::class.java.name} received Activity#onDestroy() callback" ) } } } companion object { //伴生对象中install方法中创建ActivityDestroyWatcher对象并注册Activiy监听传入ObjectWatcher ,Config fun install( application: Application, objectWatcher: ObjectWatcher, configProvider: () -> Config ) { val activityDestroyWatcher = ActivityDestroyWatcher(objectWatcher, configProvider) application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks) } } }
AndroidOFragmentDestroyWatcher
AndroidXFragmentDestroyWatcher
AndroidSupportFragmentDestroyWatcher
此处限于篇幅不列出具体代码要看的可以自己导入下leakCanary去具体看下代码。如何检测泄漏
objectWatcher.watch( view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " + "(references to its views should be cleared to prevent leaks)" )
追踪过程中你可能需要了解一下强软弱虚四种引用
其中包含了我的一个疑问:Activity或者Application被弱引用包装为啥GC的时候不会被回收?
Android中使用比较常见,我们经常会碰到需要使用Context及其子类如Application、Activity等组件类的时候,这时候如果使用强引用很容易出现内存泄漏的情况,而使用软引用又会造成不必要的资源浪费,因此此时使用弱引用是最好的解决方案,因为像Activity这类自带生命周期的组件都是被更底层的对象持有着强引用的,如AM(ActivityManager),因此当这类对象还处在自己的生命周期之中时是不会被轻易回收的,只有在生命周期结束之后才会被GC判定为只存在弱引用而最终被回收掉。class ObjectWatcher constructor( private val clock: Clock, private val checkRetainedExecutor: Executor, /** * Calls to [watch] will be ignored when [isEnabled] returns false */ private val isEnabled: () -> Boolean = { true } ) { private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() private val queue = ReferenceQueue<Any>() @Synchronized fun watch(watchedObject: Any) { watch(watchedObject, "") } /** * Watches the provided [watchedObject]. * * @param description Describes why the object is watched. */ @Synchronized fun watch( watchedObject: Any, description: String ) { if (!isEnabled()) { return } //先清除一次防止重复添加(应该是这么个作用因为key每次都是随机生成的) removeWeaklyReachableObjects() //生成每个被观察对象的key val key = UUID.randomUUID() .toString() //主要是在dump的时候使用计算一个对象被引用了多久 val watchUptimeMillis = clock.uptimeMillis() //KeyWeakReference是WeakRefrence的子类,LeakCanary的原理就是如果对象被回收了的话 //会把引用对象放到 queue(ReferenceQueue<Any>) 生成一个对watchedObject的弱引用 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" } //把相应的refrece放到被观察对象map中,watchedObjects可变map watchedObjects[key] = reference //然后通过Handler发送延迟五秒消息去判断是否还有引用没回收并dump,下边会介绍checkRetainedExecutor对象的定义 checkRetainedExecutor.execute { moveToRetained(key) } } //poll出queue中的被回收的对象移除watchedObjects中的相应弱引用对象 private fun removeWeaklyReachableObjects() { // 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. var ref: KeyedWeakReference? do { ref = queue.poll() as KeyedWeakReference? if (ref != null) { watchedObjects.remove(ref.key) } } while (ref != null) } @Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) { onObjectRetainedListeners.add(listener) } //handler延迟五秒后执行run方法中的moveToRetained方法 @Synchronized private fun moveToRetained(key: String) { //先去poll出queue中的被回收的对象移除watchedObjects中的相应弱引用对象 防止5秒过程中可能对象已经被回收了 下边还要进行一次gc和dump removeWeaklyReachableObjects() //拿出当前watch对象的弱引用 val retainedRef = watchedObjects[key] //【标记最后一步】若该对象若引用不为空则继续下一步为null则认为已被回收了不作处理 if (retainedRef != null) { retainedRef.retainedUptimeMillis = clock.uptimeMillis() //执行onObjectRetainedListeners中每个监听的onObjectRetained方法 onObjectRetainedListeners.forEach { it.onObjectRetained() } } } }
都是定义在InternalAppWatcher中的前边有说到过这个类。private val checkRetainedExecutor = Executor { //AppWatcher.config.watchDurationMillis //在AppWatcher的Config类中定义val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5),默认五秒 mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis) } val objectWatcher = ObjectWatcher( clock = clock, checkRetainedExecutor = checkRetainedExecutor, isEnabled = { true } )
我们直接跟踪ObjectWatcher中的addOnObjectRetainedListener代码@Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) { onObjectRetainedListeners.add(listener) }
override fun invoke(application: Application) { AppWatcher.objectWatcher.addOnObjectRetainedListener(this) val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application)) //创建一个后台子线程的Handler,backgroundHandler这个后边是用来检测未回收对象的 val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME) handlerThread.start() val backgroundHandler = Handler(handlerThread.looper) heapDumpTrigger = HeapDumpTrigger( application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper, configProvider ) }
最后我们在InternalAppWatcher发现他是通过反射调用了internal object InternalAppWatcher { private val onAppWatcherInstalled: (Application) -> Unit ...省略代吗 init {//我们创建InternalAppWatcherinit方法就会调用创建InternalLeakCanary对象并把对象赋值给onAppWatcherInstalled,onAppWatcherInstalled这个是个Lamda表达式 其实就是InternalLeakCanary中的invoke方法表达式 val internalLeakCanary = try { val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary") leakCanaryListener.getDeclaredField("INSTANCE") .get(null) } catch (ignored: Throwable) { NoLeakCanary } @kotlin.Suppress("UNCHECKED_CAST") onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit } fun install(application: Application) { ...省略代吗 ActivityDestroyWatcher.install(application, objectWatcher, configProvider) FragmentDestroyWatcher.install(application, objectWatcher, configProvider) //在install的时候通过onAppWatcherInstalled调用InternalLeakCanary invoke方法 onAppWatcherInstalled(application) } ...省略代吗 }
好了经过上变得分写我们已经知道InternalLeakCanary在哪里创建并注册对象未回收监听(addOnObjectRetainedListener)。
接下来我们分析InternalLeakCanary实现了监听方法后方法里调用了什么。override fun onObjectRetained() { if (this::heapDumpTrigger.isInitialized) { heapDumpTrigger.onObjectRetained() } } fun onDumpHeapReceived(forceDump: Boolean) { if (this::heapDumpTrigger.isInitialized) { heapDumpTrigger.onDumpHeapReceived(forceDump) } }
//调用 heapDumpTrigger.onObjectRetained() //onObjectRetained方法的实现 fun onObjectRetained() { scheduleRetainedObjectCheck( reason = "found new object retained", rescheduling = false ) }
private fun scheduleRetainedObjectCheck( reason: String, rescheduling: Boolean, delayMillis: Long = 0L ) { val checkCurrentlyScheduledAt = checkScheduledAt if (checkCurrentlyScheduledAt > 0) { val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis() SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" } return } else { val verb = if (rescheduling) "Rescheduling" else "Scheduling" val delay = if (delayMillis > 0) " in ${delayMillis}ms" else "" SharkLog.d { "$verb check for retained objects${delay} because $reason" } } checkScheduledAt = SystemClock.uptimeMillis() + delayMillis //在后台子线程的handler中检测未回收对象 backgroundHandler.postDelayed({ checkScheduledAt = 0 checkRetainedObjects(reason) }, delayMillis) }
private fun checkRetainedObjects(reason: String) { val config = configProvider() // A tick will be rescheduled when this is turned back on. //配置中是否要dump堆栈信息并通知 if (!config.dumpHeap) { SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" } return } //目前还有多少未回收对象 var retainedReferenceCount = objectWatcher.retainedObjectCount //如果还有未回收对象则在进行一次GC不一定能执行确保对象能回收的都回收了,然后再去赋值retainedReferenceCount if (retainedReferenceCount > 0) { gcTrigger.runGc() retainedReferenceCount = objectWatcher.retainedObjectCount } //检测retainedReferenceCount是否为零或者小于retainedVisibleThreshold = 5如果小于则跳过下边的检测延迟 WAIT_FOR_OBJECT_THRESHOLD_MILLIS = 2_000L 2秒 重新从scheduleRetainedObjectCheck重新开始检测,一直循环,直到大于等于5个或者等于0个,为了防止频发回收堆造成卡顿。为零的话就直接显示通知。 if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return //大于5个后,如果处于debug模式,会再等20秒,再次执行scheduleRetainedObjectCheck操作。防止debug模式会减慢回收 if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) { onRetainInstanceListener.onEvent(DebuggerIsAttached) showRetainedCountNotification( objectCount = retainedReferenceCount, contentText = application.getString( R.string.leak_canary_notification_retained_debugger_attached ) ) scheduleRetainedObjectCheck( reason = "debugger is attached", rescheduling = true, delayMillis = WAIT_FOR_DEBUG_MILLIS ) return } val now = SystemClock.uptimeMillis() val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis //距离上次dumpheap的时间小于WAIT_BETWEEN_HEAP_DUMPS_MILLIS =60_000L 1分钟 则继续去检测未回收对象直到大于一分钟 if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) { onRetainInstanceListener.onEvent(DumpHappenedRecently) showRetainedCountNotification( objectCount = retainedReferenceCount, contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait) ) scheduleRetainedObjectCheck( reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)", rescheduling = true, delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis ) return } SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" } //显示未回收对象个数通知隐藏 dismissRetainedCountNotification() //dump堆栈信息 dumpHeap(retainedReferenceCount, retry = true) }
1)如果未回收对象个数小于5,不做操作等待5秒再次进行检查未回收的个数,一直循环,直到大于等于5个或者等于0个,为了防止频发回收堆造成卡顿。
2)大于5个后,如果处于debug模式,会再等20秒,再次执行scheduleRetainedObjectCheck检测操作。防止debug模式会减慢回收
3)距离上次堆栈分析是否大于等于1分钟,如果没有超过一分钟,也需要再次延迟(1分钟-当前距离上次的时间)再次循环执行scheduleRetainedObjectCheck检测操作
1)、获取到内容文件 Debug.dumpHprofData(heapDumpFile.absolutePath)
2)、objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)该操作是去掉以前已经分析过的对象,也就是去除掉之前的没有回收掉的对象,不在本次分析范围内
3)、HeapAnalyzerService开启IntentService服务进行分析
4)、把结果插入数据库(泄漏区分了应用本身的内存泄漏和类库的内存泄漏),并且发送通知
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算