三方库总目录: 三方库
Leakcanary 解析
按照解析第一个 Arouter 的套路来,首先是使用,然后想如果自己做怎么做,然后分析别人是怎样实现这个功能的。
| 文章目录 | 代码解析 |
|---|---|
| 官方解读 | https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/ |
| 使用方式 | 可太简单了,直接在使用的 app build 文件下添加 debugImplementation ‘com.squareup.leakcanary:leakcanary-android:2.7’ 即可,leak 会自动的初始化开始工作,太强大了吧 |
| 核心原理 | 持有弱引用队列,如果在 onDestory 5s 之后这里没有被触发释放,那么就认为他内存泄漏了,这时候就会在 leakcanary 自己的线程中开始 dump hprof 文件,这个文件为我们查看更多的内存信息提供依据 |
| 内存泄漏的几种场景 | 匿名内部类、非静态 handler、单例持有 activity 引用、监听器注册之后没有反注册、匿名 thread Runnable asyntask 、容器中内存不释放(会导致内存吃紧) |
| 内存抖动 | 内存抖动的场景,大对象的频繁创建于释放,解决方案,对象池 |
Leakcanary 使用
// 这就完事了
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
初始化解析
首先在 leakcanary-object-watcher-android 中有 AppWatcherInstaller(他是个密封类,最终实际上调用是他的继承者 MainProcess ) 类,这个类继承了 ContentProvider 类,这样就会在启动 App 的时候触发他具体代码如:
// 调用 content provider 的 onCreate 方法时会调用 AppWatcher manualInstall 方法完成初始化
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
// 初始化代码,默认 5s 超时,默认的 watchers 为 构建包含四种的 watcher
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
// 1. 主线程判断
checkMainThread()
if (isInstalled) {
throw IllegalStateException(
"AppWatcher already installed, see exception cause for prior install call", installCause
)
}
// 2. 设置的 delay 超时提醒时间需要大于 0
check(retainedDelayMillis >= 0) {
"retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
}
installCause = RuntimeException("manualInstall() first called here")
this.retainedDelayMillis = retainedDelayMillis
// 3. debug 版本启动 LogcatShark
if (application.isDebuggableBuild) {
LogcatSharkLog.install()
}
// 4. 初始化 InternalLeakCanary
/*
InternalLeakCanary#invoke
此回调中会初始化一些检测内存泄露过程中需要的对象:
*/
// Requires AppWatcher.objectWatcher to be set
LeakCanaryDelegate.loadLeakCanary(application)
// 5. 通知几个监听器去 install 自己
watchersToInstall.forEach {
it.install()
}
}
几个 install:
// activity
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
// fragment and viewmodel
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)
}
companion object {
private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
// 这里面都会针对 viewmodel 的 onclear 进行监听
private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidXFragmentDestroyWatcher"
// Using a string builder to prevent Jetifier from changing this string to Android X Fragment
@Suppress("VariableNaming", "PropertyName")
private val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME =
StringBuilder("android.").append("support.v4.app.Fragment")
.toString()
private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidSupportFragmentDestroyWatcher"
}
// RootViewWatcher 的 install,
override fun install() {
Curtains.onRootViewsChangedListeners += listener
}
// RootViewSpy
RootViewsSpy.install()
return RootViewsSpy().apply {
WindowManagerSpy.swapWindowManagerGlobalMViews { mViews ->
delegatingViewList.apply { addAll(mViews) }
}
}
// WindowManagerSpy 功能
// Enables replacing WindowManagerGlobal.mViews with a custom ArrayList implementation.
// 反射拿到这个对象,然后去注册监听
val className = if (SDK_INT > 16) {
"android.view.WindowManagerGlobal"
} else {
"android.view.WindowManagerImpl"
}
// service watcher 的 install
override fun install() {
checkMainThread()
check(uninstallActivityThreadHandlerCallback == null) {
"ServiceWatcher already installed"
}
check(uninstallActivityManager == null) {
"ServiceWatcher already installed"
}
try {
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
onServiceDestroyed(token)
}
}
try {
if (args == null) {
method.invoke(activityManagerInstance)
} else {
method.invoke(activityManagerInstance, *args)
}
} catch (invocationException: InvocationTargetException) {
throw invocationException.targetException
}
}
}
} catch (ignored: Throwable) {
SharkLog.d(ignored) { "Could not watch destroyed services" }
}
}
一些 检测时机:
ActivityWatcher
Activity#onActivityDestroyed
FragmentAndViewModelWatcher
Fragments (Support Library, Android X and AOSP)
Fragment#onDestroy()
Fragment views (Support Library, Android X and AOSP)
Fragment#onDestroyView()
Android X view models (both activity and fragment view models)
ViewModel#onCleared()
Expects root views to become weakly reachable soon after they are removed from the window
RootViewWatcher
rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
ServiceWatcher
AMS#serviceDoneExecuting
核心实现解析
ObjectWatcher
// 0. 从各个源头调用 expectWeaklyReachable 方法

// 存起来如果泄露要通知的监听者
private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()
/**
* References passed to [watch].
*/
// 核心数据结构,这里存起来我们要监听的 key 弱引用
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
// 弱引用队列
private val queue = ReferenceQueue<Any>()
// 1. 将被监听的对象使用 弱引用 持有之
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
// 2. 首先从 queue 里面搞出去一个弱引用队头的 对象
removeWeaklyReachableObjects()
/*
3. key / watchTime / refrence
*/
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
/*
这里构建是咱们的核心
class KeyedWeakReference(
referent: Any,
val key: String,
val description: String,
val watchUptimeMillis: Long,
referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
referent, referenceQueue
)
这里可以看到这个 keyed 是继承于 WeakRefreence 的,继承的时候将,reference queue 设置为我们这个了,所以 在 Java GC 回收的时候才会把这个放到 queue 里,这样我们才有了依据
*/
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"
}
// 4. 添加刚刚构建的 refrence 到 map
watchedObjects[key] = reference
// 5. executor 里去 exec 去
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
// 5.1 看看这个 checkRetainedExecutor 是个啥,lambda 表达式,delay 5s 之后检测是否还在
checkRetainedExecutor = {
check(isInstalled) {
"AppWatcher not installed"
}
mainHandler.postDelayed(it, retainedDelayMillis)
}
// 如果超时了这个对象还没有被移除,那么就是超时了
@Synchronized private fun moveToRetained(key: String) {
// 这里去尝试移除对应的对象,这里移除成功之后理论上 watchedObjects 这里就木有了,有就代表泄漏,妙啊,妙啊。
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
// 尝试移除已经解除嫌疑滴对象,因为这里到达就说明正常释放了
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)
}
昨天的车上碎念
今天工作时间一天就产出了一篇文章,这是不是效率也太低了,主要是这篇文章也是之前学习过的 arouter 而写的。
继续要写的是 leakcanary 的解析,这个希望明天上午可以写完,现在已经写到核心类 object wacther 这里了,下一步就是解析他的原理,实际上,这个里面就是存储着键值对,其中 value 为各个监控对象的 弱引用 将他们放到弱引用队列等各种周期的触发,例如 activity 的 ondestory , fragment 的 ondestory 及 ondestoryview 以及 viewmodel 的 onclear ,service 的 ams destory service,view 的 ondetachview 等时机,这个主要是是反射了 windowrootimpl 等,将监听加进去。
收到触发信号后就开始计时,有一个默认的超时时间,就是 5s ,5s 后这个弱引用没有被添加到引用队列的话,那么就判定为泄露了,这时候就会通知各种监听的 listener,他们去做 dump 了,更新 notification 了,等等。
这个分析完之后分析 blockcanary 这个主要是在 looper 里埋地雷,超时就会 dump 数据然后会统计超时了多少,还有一个功能是 watchdog 就是一个 while true 循环,每当有事件发生时,他就搞一个 int 的 task 到 looper 里,超时之后发现这个值没有变化的话就证明这个没有执行到,产生 anr 了,然后统计当前内存和线程信息,分析 anr 产生的原因。
其实细数上面两个 canary ,其中 leak 属于开创,用了各种生命周期的时机去触发,然后利用 Java 虚拟机的内存回收机制,就是回收的对象一定会先放到 弱引用 队列中去,结合这两者就能巧妙的解决内存泄露的问题了。
内存抖动问题可以通过 as 的 profiler 去观察,看着内存一直有 波峰波谷 的震动,那么就证明有内存波动的存在,以及虽然没有内存泄露,但是有 collection 大量持有大内存不释放会引起 oom,通过分析 hprof 也可以查看那些占用内存最大,然后想办法干掉或者减少这些内存大户的占用,最主要的就是图片内存的占用了。
todo
接下来分析的三方库:
glide
retrofit
okhttp
hilt
gson
eventbus
room
leakcanary 总结
leak canary 的核心就是在各个关键地方去巧妙的找到可能内存泄漏的点,找到之后让各个 watcher 去在这些时机将这些对象都放到一个 map 中,其中 key 为 uuid 搞得一个任意值,value 为 一个弱引用(如果是强引用,他都会导致内存泄漏。),然后利用 Java 的 GC 机制,一个对象在回收的时候会把它塞到 弱引用队列中去,这时候咱们在一个 超时间隔之后去 check 他如果在 queue 里面,那么就把它移除,然后移除 map 中的存储,校验这个对象在 map 中没有了,证明他已经真正的回收了,否则就是泄漏了,完美。