1. LeakCanary的介紹和使用
1.1 簡(jiǎn)介
- LeakCanray是Square開(kāi)源的Java內(nèi)存泄漏分析工具菩貌,用于在開(kāi)發(fā)階段檢測(cè)Android應(yīng)用中常見(jiàn)中的內(nèi)存泄漏金闽。
- 它支持以下五種Android場(chǎng)景中的內(nèi)存泄漏監(jiān)測(cè):
- 已銷(xiāo)毀的 Activity 對(duì)象(進(jìn)入 DESTROYED 狀態(tài))
- 已銷(xiāo)毀的 Fragment 對(duì)象和 Fragment View 對(duì)象(進(jìn)入 DESTROYED 狀態(tài))
- 已清除的的 ViewModel 對(duì)象(進(jìn)入 CLEARED 狀態(tài))
- 已銷(xiāo)毀的的 Service 對(duì)象(進(jìn)入 DESTROYED 狀態(tài))
- 已從 WindowManager 中移除的 RootView 對(duì)象
1.2 使用
- 只需要在 build.gradle 中添加 LeakCanary 依賴(lài)即可(LeakCanary內(nèi)部默認(rèn)使用了ContentProvider實(shí)現(xiàn)無(wú)侵入初始化)。注意:LeakCanary 是只在開(kāi)發(fā)環(huán)境使用的工具息拜,所以記得使用
debugImplementation
來(lái)配置依賴(lài)显设。
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
// 繼承startup的方式剪返,與上面二選一扁誓,由于是開(kāi)發(fā)環(huán)境,所以意義不大
// debugImplementation 'com.squareup.leakcanary:leakcanary-android-startup:2.12'
// 可選,使用WorkerManager多進(jìn)程分析堆快照提升分析速度
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.12'
2. 源碼分析
2.1 初始化
- 1.x 時(shí)期的 LeakCanary 是需要在 Application.onCreate()中加入初始化代碼的尿瞭,2.x之后LeakCanary借助了
ContentProvider
的啟動(dòng)機(jī)制來(lái)間接調(diào)用初始化 API闽烙, 實(shí)現(xiàn)了無(wú)侵入的 LeakCanary 初始化。 - 具體的位置是在 leakcanary-object-watcher-android 這個(gè)子module中
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.leakcanary.objectwatcher"
>
<application>
<!--可以通過(guò)配置leak_canary_watcher_auto_install變量設(shè)置是否自行初始化声搁,默認(rèn)為true -->
<provider
android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
</application>
internal class MainProcessAppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
// LeakCanary初始化代碼
AppWatcher.manualInstall(application)
return true
}
...
}
- 真正的初始化代碼為
AppWatcher.manualInstall(application)
// AppWatcher.kt
/** LeakCanary初始化 */
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默認(rèn)5s后進(jìn)行泄漏檢測(cè)
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
checkMainThread()
...
// 初始化 InternalLeakCanary 內(nèi)部引擎
LeakCanaryDelegate.loadLeakCanary(application)
// 遍歷五種監(jiān)聽(tīng)器進(jìn)行分別注冊(cè)
watchersToInstall.forEach {
it.install()
}
...
}
/** 創(chuàng)建監(jiān)聽(tīng)集合 */
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
// 對(duì)應(yīng)5中Android泄漏場(chǎng)景(Activity黑竞、Fragment和ViewModel、View疏旨、Service)
// 傳入的reachabilityWatcher均為ReachabilityWatcher的唯一實(shí)現(xiàn)類(lèi)ObjectWatcher
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
- 分為四個(gè)監(jiān)控類(lèi)分別對(duì)Activity很魂、Fragment和ViewModel、RootView以及Service分別監(jiān)控檐涝,接下來(lái)分析下這四個(gè)類(lèi)是如何具體監(jiān)控的
2.2 四大監(jiān)控類(lèi)實(shí)現(xiàn)五種泄漏場(chǎng)景的監(jiān)控
2.2.1 ActivityWatcher
- ActivityWatcher主要實(shí)現(xiàn)對(duì)Activity的監(jiān)控
/** Activity的泄漏監(jiān)聽(tīng) */
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
// 交給objectWatcher分析
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
// 注冊(cè)監(jiān)聽(tīng)的方式
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
- Activity的監(jiān)聽(tīng)比較簡(jiǎn)單遏匆,直接使用提供的全局監(jiān)聽(tīng)的Api
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)
即可,然后在每個(gè)Activity的onDestroy中將activity交給objectWatcher去分析即可
2.2.2 FragmentAndViewModelWatcher
- FragmentAndViewModelWatcher主要實(shí)現(xiàn)對(duì)Fragment谁榜、Fragment view以及ViewModel的監(jiān)控
/**
* 主要負(fù)責(zé)Fragment的泄漏監(jiān)聽(tīng)(通過(guò)Fragment.onDestroy())
* Fragment View的泄漏監(jiān)聽(tīng)(通過(guò)Fragment.onDestroyView())
* ViewModel的泄漏監(jiān)聽(tīng)(通過(guò)ViewModel.onCleared())
*/
class FragmentAndViewModelWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
/** Fragment監(jiān)測(cè)集合(主要包含幾種包下的Fragment) */
// 集合的類(lèi)型是(Activity)->Unit的函數(shù)類(lèi)型
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
// Android O(8.0)后使用AndroidOFragmentDestroyWatcher監(jiān)聽(tīng)
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
}
// androidx包下的Fragment使用AndroidXFragmentDestroyWatcher監(jiān)聽(tīng)
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
// android.support.v4包下的Fragment使用AndroidSupportFragmentDestroyWatcher監(jiān)聽(tīng)
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
fragmentDestroyWatchers
}
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
// 監(jiān)聽(tīng)每個(gè)Activity的onActivityCreated(),再在其中進(jìn)行Fragment監(jiān)聽(tīng)
for (watcher in fragmentDestroyWatchers) {
// 由于fragmentDestroyWatchers里面本身存儲(chǔ)的是一個(gè)(Activity)->Unit的函數(shù)類(lèi)型
// 所以這里可以直接使用watcher(activity)幅聘,直接調(diào)用,參數(shù)為activity窃植,
// 實(shí)際執(zhí)行的是watcher中的invoke()
watcher(activity)
}
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
...
}
-
Fragment 與 Fragment View 監(jiān)控 主要是通過(guò)
FragmentAndViewModelWatcher
實(shí)現(xiàn)帝蒿,首先是通過(guò)Application.registerActivityLifecycleCallbacks(…)
接口監(jiān)聽(tīng) Activity.onCreate()事件,再通過(guò)FragmentManager.registerFragmentLifecycleCallbacks(…)
接口監(jiān)聽(tīng) Fragment 的生命周期 - 這里根據(jù)Android版本以及Fragment包不同分了三種狀態(tài)巷怜,分別是
AndroidOFragmentDestroyWatcher
葛超、AndroidXFragmentDestroyWatcher
、AndroidSupportFragmentDestroyWatcher
丛版,這里以AndroidXFragmentDestroyWatcher為例
/** Androidx包下的Fragment巩掺、FragmentView以及ViewModel監(jiān)聽(tīng) */
internal class AndroidXFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
) {
// 注冊(cè)Fragment級(jí)別的ViewModel Hook
ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
}
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null) {
// 監(jiān)聽(tīng)FragmentView.onDestroy()將Fragment.View交給ObjectWatcher分析
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
) {
// 監(jiān)聽(tīng)Fragment.onDestroy()將Fragment交給ObjectWatcher分析
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
override fun invoke(activity: Activity) {
// 這段代碼會(huì)在Activity.onCreate()中執(zhí)行
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
// 注冊(cè)Fragment生命周期監(jiān)聽(tīng)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
// 注冊(cè)Activity級(jí)別的ViewModel Hook
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
}
}
}
- 這里面還包含了ViewModel的監(jiān)聽(tīng)邏輯,分別在 Activity.onCreate() 和 Fragment.onCreate()中使用
ViewModelClearedWatcher
進(jìn)行監(jiān)聽(tīng)页畦,由于系統(tǒng)并未提供ViewModel全局監(jiān)聽(tīng)的方法胖替,所以 ViewModelClearedWatcher 是通過(guò)Hook
方式實(shí)現(xiàn)的,我們看下ViewModelClearedWatcher
internal class ViewModelClearedWatcher(
storeOwner: ViewModelStoreOwner,
private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
// 直接通過(guò)反射獲取ViewModelStore類(lèi)中的map變量(后面改成mMap)豫缨,即可獲得作用域中的所有ViewModel對(duì)象
private val viewModelMap: Map<String, ViewModel>? = try {
val storeClass = ViewModelStore::class.java
val mapField = try {
storeClass.getDeclaredField("map")
} catch (exception: NoSuchFieldException) {
storeClass.getDeclaredField("mMap")
}
mapField.isAccessible = true
@Suppress("UNCHECKED_CAST")
mapField[storeOwner.viewModelStore] as Map<String, ViewModel>
} catch (ignored: Exception) {
SharkLog.d(ignored) { "Could not find ViewModelStore map of view models" }
null
}
override fun onCleared() {
// 遍歷當(dāng)前作用域所有ViewModel對(duì)象
viewModelMap?.values?.forEach { viewModel ->
// 使用ObjectWatcher.expectWeaklyReachable
reachabilityWatcher.expectWeaklyReachable(
viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
)
}
}
companion object {
// 在storeOwner作用域?qū)嵗疺iewModelClearedWatcher對(duì)象
fun install(
storeOwner: ViewModelStoreOwner,
reachabilityWatcher: ReachabilityWatcher
) {
val provider = ViewModelProvider(storeOwner, object : Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
// 直接在storeOwner作用域?qū)嵗疺iewModelClearedWatcher對(duì)象
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
})
provider.get(ViewModelClearedWatcher::class.java)
}
}
}
- 由此可見(jiàn):ViewModel的監(jiān)測(cè)是在Activity.onCreate()/Fragment.onCreate()中分別創(chuàng)建一個(gè)自定義的ViewModel(即ViewModelClearedWatcher)独令,然后監(jiān)聽(tīng)ViewModelClearedWatcher的onCleared(),通過(guò)Hook ViewModelStore的方式獲取相應(yīng)應(yīng)作用域(Activity/Fragment)中的所有ViewModel對(duì)象好芭,并最終交給ObjectWatcher進(jìn)行監(jiān)控
2.2.3 RootViewWatcher
- RootViewWatcher是處理RootView內(nèi)存泄漏的監(jiān)聽(tīng)器燃箭,由于系統(tǒng)并未提供全局RootView的監(jiān)聽(tīng),所以LeakCannary還是通過(guò)Hook方式來(lái)處理的舍败,只不過(guò)這里沒(méi)有自行Hook而是利用了square的另一個(gè)開(kāi)源庫(kù)curtains來(lái)處理的招狸,主要通過(guò)Hook WMS 服務(wù)內(nèi)部的
WindowManagerGlobal
(sdk>16)或者WindowManagerImpl
(sdk<=16)來(lái)獲取所有的RootView新增或移除的時(shí)機(jī)
/**
* RootView泄漏監(jiān)聽(tīng)敬拓,主要利用了Curtains庫(kù)實(shí)現(xiàn)對(duì)
*/
class RootViewWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val listener = OnRootViewAddedListener { rootView ->
// 判斷rootView的窗口類(lèi)型
// 是否需要使用RootViewWatcher監(jiān)聽(tīng)
val trackDetached = when(rootView.windowType) {
PHONE_WINDOW -> {
when (rootView.phoneWindow?.callback?.wrappedCallback) {
// Activities are already tracked by ActivityWatcher
// 由于Activity已經(jīng)有ActivityWatcher監(jiān)聽(tīng),這里直接返回false,即無(wú)需通過(guò)RootViewWatcher監(jiān)聽(tīng)
is Activity -> false
is Dialog -> {
// Use app context resources to avoid NotFoundException
// https://github.com/square/leakcanary/issues/2137
// 通過(guò)配置開(kāi)啟裙戏,默認(rèn)不開(kāi)啟
val resources = rootView.context.applicationContext.resources
resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
}
// Probably a DreamService
// 屏保等
else -> true
}
}
// Android widgets keep detached popup window instances around.
POPUP_WINDOW -> false
// Tooltip乘凸、Toast等進(jìn)行監(jiān)聽(tīng)
TOOLTIP, TOAST, UNKNOWN -> true
}
if (trackDetached) {
// 注冊(cè)監(jiān)聽(tīng)事件
rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
val watchDetachedView = Runnable {
// 接收發(fā)送的消息,使用ObjectWatcher對(duì)rootView進(jìn)行監(jiān)聽(tīng)
reachabilityWatcher.expectWeaklyReachable(
rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
)
}
override fun onViewAttachedToWindow(v: View) {
// 添加時(shí)移除消息
mainHandler.removeCallbacks(watchDetachedView)
}
override fun onViewDetachedFromWindow(v: View) {
// 監(jiān)聽(tīng)RootView的移除事件累榜,使用Handler發(fā)送消息處理
mainHandler.post(watchDetachedView)
}
})
}
}
override fun install() {
// 注冊(cè)RootView監(jiān)聽(tīng)
Curtains.onRootViewsChangedListeners += listener
}
override fun uninstall() {
Curtains.onRootViewsChangedListeners -= listener
}
}
- 主要是針對(duì)
Dialog(有監(jiān)聽(tīng)開(kāi)關(guān)配置)营勤、DreamService、Tooltip壹罚、Toast
等類(lèi)型的RootView進(jìn)行監(jiān)聽(tīng)葛作,通過(guò)調(diào)用rootView.addOnAttachStateChangeListener
監(jiān)聽(tīng)onViewDetachedFromWindow
方法,監(jiān)聽(tīng)RootView的移除事件猖凛,在移除事件中將RootView交給ObjectWatcher進(jìn)行監(jiān)控
2.2.4 ServiceWatcher
- ServiceWatcher是處理Service內(nèi)存泄漏的監(jiān)聽(tīng)器赂蠢,系統(tǒng)也并未提供全局Service的移除監(jiān)聽(tīng),所以LeaakCanary也是通過(guò)Hook方式實(shí)現(xiàn)的
@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
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<IBinder, Service>
}
private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
private var uninstallActivityManager: (() -> Unit)? = null
override fun install() {
checkMainThread()
check(uninstallActivityThreadHandlerCallback == null) {
"ServiceWatcher already installed"
}
check(uninstallActivityManager == null) {
"ServiceWatcher already installed"
}
try {
// Hook ActivityThread類(lèi)中的mH.mCallback
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
if (msg.obj !is IBinder) {
return@Callback false
}
// 監(jiān)聽(tīng)Service.onStop()事件消息
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
// activityThreadServices是通過(guò)反射獲取的ActivityThread類(lèi)中的mServices成員變量<IBinder, Service>
activityThreadServices[key]?.let {
// 服務(wù)銷(xiāo)毀前的處理辨泳,這里主要是暫存
onServicePreDestroy(key, it)
}
}
// Hook后繼續(xù)執(zhí)行Framework本身的邏輯
mCallback?.handleMessage(msg) ?: false
}
}
// Hook AMS IActivityManager
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
// 代理serviceDoneExecuting()方法
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
// 處理Service銷(xiāo)毀客年,主要是將service交給ObjectWatcher進(jìn)行監(jiān)控
onServiceDestroyed(token)
}
}
// 繼續(xù)執(zhí)行serviceDoneExecuting()本身的方法
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" }
}
}
override fun uninstall() {
checkMainThread()
uninstallActivityManager?.invoke()
uninstallActivityThreadHandlerCallback?.invoke()
uninstallActivityManager = null
uninstallActivityThreadHandlerCallback = null
}
private fun onServicePreDestroy(
token: IBinder,
service: Service
) {
servicesToBeDestroyed[token] = WeakReference(service)
}
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"
)
}
}
}
/**
* Hook修改ActivityThread類(lèi)中的mH.mCallback
* swap 是一個(gè) lambda 表達(dá)式,參數(shù)為原對(duì)象漠吻,返回值為注入的新對(duì)象
*/
private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
val mHField =
activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
val mH = mHField[activityThreadInstance] as Handler
val mCallbackField =
Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
val mCallback = mCallbackField[mH] as Handler.Callback?
mCallbackField[mH] = swap(mCallback)
}
/**
* Hook修改ActivityThread類(lèi)中的mH.mCallback
* swap 是一個(gè) lambda 表達(dá)式,參數(shù)為 IActivityManager 的 Class 對(duì)象和接口原實(shí)現(xiàn)對(duì)象司恳,返回值為注入的新對(duì)象
*/
@SuppressLint("PrivateApi")
private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
val singletonClass = Class.forName("android.util.Singleton")
val mInstanceField =
singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }
val singletonGetMethod = singletonClass.getDeclaredMethod("get")
val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
"android.app.ActivityManager" to "IActivityManagerSingleton"
} else {
"android.app.ActivityManagerNative" to "gDefault"
}
val activityManagerClass = Class.forName(className)
val activityManagerSingletonField =
activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]
val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
// 將swap的返回值作為新對(duì)象途乃,實(shí)現(xiàn) Hook
mInstanceField[activityManagerSingletonInstance] =
swap(iActivityManagerInterface, activityManagerInstance!!)
}
companion object {
private const val STOP_SERVICE = 116
private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
}
}
- Hook的大致步驟為:
- Hook主線(xiàn)程消息循環(huán)的
mH.mCallback
回調(diào),監(jiān)聽(tīng)其中的STOP_SERVICE
消息扔傅,將即將 Destroy 的 Service 對(duì)象暫存起來(lái)(由于 ActivityThread.H 中沒(méi)有 DESTROY_SERVICE 消息耍共,所以不能直接監(jiān)聽(tīng)到 onDestroy() 事件,需要下面的步驟)猎塞; - 使用動(dòng)態(tài)代理 Hook AMS 與 App 通信的的
IActivityManager
Binder 對(duì)象试读,代理其中的serviceDoneExecuting()
方法,視為 Service.onDestroy() 的執(zhí)行時(shí)機(jī)荠耽,拿到暫存的 Service 對(duì)象交給 ObjectWatcher 監(jiān)控钩骇。
- Hook主線(xiàn)程消息循環(huán)的
2.3 ObjectWatcher如何監(jiān)控內(nèi)存泄漏
2.3.1 Java四大引用相關(guān)知識(shí)
-
強(qiáng)引用
- 普通的最常見(jiàn)的引用,當(dāng)對(duì)象具有強(qiáng)引用且未被置空铝量,虛擬機(jī)即使OOM也不會(huì)回收
-
軟引用
- 當(dāng)內(nèi)存充足時(shí)虛擬機(jī)不會(huì)回收它倘屹,內(nèi)存不足時(shí)會(huì)自動(dòng)回收,常用于圖片緩存
SoftReference<String> softReference = new SoftReference<>(str);
-
弱引用
- 無(wú)論內(nèi)存是否充足慢叨,只要手動(dòng)調(diào)用垃圾回收System.gc()或等待虛擬機(jī)自動(dòng)GC纽匙,弱引用就會(huì)被回收。主要用來(lái)放置在容易引起內(nèi)存泄漏的位置拍谐,如Android中的Handler
WeakReference<String> weakReference = new WeakReference<>(str);
-
虛引用
- 虛引用并不會(huì)決定對(duì)象的生命周期烛缔。如果一個(gè)對(duì)象僅持有虛引用馏段,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收践瓷。主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)院喜,使用 PhantomReference 來(lái)創(chuàng)建
-
引用隊(duì)列ReferenceQueue
- 當(dāng)gc準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還僅有軟引用(或弱引用当窗,或虛引用)指向它够坐,就會(huì)在回收該對(duì)象之前,把這個(gè)軟引用(或弱引用崖面,或虛引用)加入到與之關(guān)聯(lián)的引用隊(duì)列(ReferenceQueue)中元咙。如果一個(gè)軟引用(或弱引用,或虛引用)對(duì)象本身在引用隊(duì)列中巫员,就說(shuō)明該引用對(duì)象所指向的對(duì)象被回收了;
// 構(gòu)造一個(gè)強(qiáng)引用
Object obj = new Object();
// 創(chuàng)建引用隊(duì)列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 利用強(qiáng)引用和引用隊(duì)列構(gòu)造弱引用
WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);
System.out.println("調(diào)用GC前弱引用:" + weakReference.get()); // java.lang.Object@6108b2d7
System.out.println("調(diào)用GC前引用隊(duì)列:" + referenceQueue.poll()); // null
// 將強(qiáng)引用手動(dòng)置null
obj = null;
// 手動(dòng)GC或虛擬機(jī)自動(dòng)GC都會(huì)回收弱引用庶香,這里手動(dòng)調(diào)用GC
System.gc();
System.out.println("調(diào)用GC后弱引用:" + weakReference.get()); // null
System.out.println("調(diào)用GC后引用隊(duì)列:" + referenceQueue.poll()); // java.lang.Object@6108b2d7
2.3.2 ObjectWatcher對(duì)象監(jiān)控
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()
/** 被監(jiān)控對(duì)象的映射表 */
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
/** 弱引用引用隊(duì)列,與KeyedWeakReference相關(guān)聯(lián)简识,對(duì)象正常銷(xiāo)毀會(huì)存在這里面 */
private val queue = ReferenceQueue<Any>()
...
/** 監(jiān)控對(duì)象泄漏 */
@Synchronized
override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
// 移除watchedObjects中未泄漏的對(duì)象
removeWeaklyReachableObjects()
// 構(gòu)造KeyedWeakReference 引用對(duì)象
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
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
// 默認(rèn)5秒后執(zhí)行檢查
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
...
@Synchronized
private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
// 移除watchedObjects中未泄露的對(duì)象后剩余的判定為發(fā)生泄漏
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
// 回調(diào)通知LeakCanary內(nèi)部處理
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
/** 移除隊(duì)列中未泄漏的對(duì)象 */
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
/** 弱引用包裝類(lèi) */
class KeyedWeakReference(
/** 被監(jiān)控對(duì)象 */
referent: Any,
/** 映射表的Key */
val key: String,
/** 描述 */
val description: String,
/** 監(jiān)控開(kāi)始時(shí)間(引用創(chuàng)建時(shí)間) */
val watchUptimeMillis: Long,
/** 關(guān)聯(lián)的引用隊(duì)列 */
referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
referent, referenceQueue
) {
/** 判定對(duì)象為泄漏對(duì)象的時(shí)間赶掖,-1表示非泄漏對(duì)象或還未判定完畢 */
@Volatile
var retainedUptimeMillis = -1L
override fun clear() {
super.clear()
retainedUptimeMillis = -1L
}
companion object {
/** 記錄最近一次觸發(fā)Heap Dump的時(shí)間 */
@Volatile
@JvmStatic var heapDumpUptimeMillis = 0L
}
}
- 主要經(jīng)歷了三個(gè)步驟
- 為被監(jiān)控對(duì)象
watchedObject
創(chuàng)建一個(gè)KeyedWeakReference
弱引用,并存儲(chǔ)到 <UUID, KeyedWeakReference> 的映射表中七扰; - postDelay 五秒后檢查引用對(duì)象是否出現(xiàn)在引用隊(duì)列中奢赂,出現(xiàn)在隊(duì)列則說(shuō)明被監(jiān)控對(duì)象未發(fā)生泄漏。隨后颈走,移除映射表中未泄露的記錄膳灶,更新泄漏的引用對(duì)象的
retainedUptimeMillis
字段以標(biāo)記為泄漏 - 通過(guò)回調(diào)
onObjectRetained
告知 LeakCanary 內(nèi)部發(fā)生新的內(nèi)存泄漏
2.4 Dump heap獲取內(nèi)存快照文件
- ObjectWatcher 判定被監(jiān)控對(duì)象發(fā)生泄漏后,會(huì)通過(guò)接口方法
OnObjectRetainedListener.onObjectRetained()
回調(diào)到 LeakCanary 內(nèi)部的管理器InternalLeakCanary
- 由于分析工作比較耗時(shí)間立由,LeakCanary自然不會(huì)每次發(fā)現(xiàn)內(nèi)存泄漏對(duì)象都進(jìn)行分析工作轧钓,而會(huì)進(jìn)行兩個(gè)攔截:
- 1. 泄漏對(duì)象計(jì)數(shù)未達(dá)到閾值,或者進(jìn)入后臺(tái)時(shí)間未達(dá)到閾值
- 2. 計(jì)算距離上一次 HeapDump 未超過(guò) 60S
// InternalLeakCanary.kt
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
// HeapDumpTrigger.kt
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
// 避免重復(fù)postDelayed
if (checkCurrentlyScheduledAt > 0) {
return
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects()
}, delayMillis)
}
private fun checkRetainedObjects() {
val iCanHasHeap = HeapDumpControl.iCanHasHeap()
val config = configProvider()
if (iCanHasHeap is Nope) {
if (iCanHasHeap is NotifyingNope) {
// Before notifying that we can't dump heap, let's check if we still have retained object.
// 泄漏計(jì)數(shù)
var retainedReferenceCount = objectWatcher.retainedObjectCount
// 泄漏計(jì)數(shù)>0時(shí)主動(dòng)調(diào)用GC
if (retainedReferenceCount > 0) {
// 這個(gè)方法是調(diào)用RunTime.getRuntime().GC()并休眠等待100ms
gcTrigger.runGc()
// GC后再獲取泄漏計(jì)數(shù)
retainedReferenceCount = objectWatcher.retainedObjectCount
}
val nopeReason = iCanHasHeap.reason()
// 這里會(huì)判斷泄漏計(jì)數(shù)是否>5(默認(rèn)閾值)
val wouldDump = !checkRetainedCount(
retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
)
if (wouldDump) {
val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
// 回調(diào)onEvent
onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
// 展示通知
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = uppercaseReason
)
}
} else {
SharkLog.d {
application.getString(
R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
)
}
}
return
}
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
// 計(jì)算距離上一次HeapDump時(shí)間未超過(guò)60s會(huì)攔截
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(
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
// 移除通知
dismissRetainedCountNotification()
val visibility = if (applicationVisible) "visible" else "not visible"
// 觸發(fā)dumpHeap分析
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
2.5 分析堆快照dumpHeap
- LeakCanary 已經(jīng)成功生成
.hprof
堆快照文件锐膜,并且發(fā)送了一個(gè) LeakCanary 內(nèi)部事件HeapDump
毕箍。LeakCanary 的配置項(xiàng)中設(shè)置了多個(gè)事件消費(fèi)者EventListener,這三個(gè)會(huì)根據(jù) App 當(dāng)前的依賴(lài)項(xiàng)而選擇最優(yōu)的執(zhí)行策略: - 1 - WorkerManager 多進(jìn)程分析
- 2 - WorkManager 異步分析
- 3 - 異步線(xiàn)程分析(兜底策略)
// LeakCanary.kt
val eventListeners: List<EventListener> = listOf(
LogcatEventListener,
ToastEventListener,
LazyForwardingEventListener {
if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
},
when {
// WorkManager多進(jìn)程分析
RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
RemoteWorkManagerHeapAnalyzer
// WorkManager 異步分析
WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
// 異步線(xiàn)程分析(兜底策略)
else -> BackgroundThreadHeapAnalyzer
}
),
2.5.1 WorkerManager 多進(jìn)程分析
object RemoteWorkManagerHeapAnalyzer : EventListener {
private const val REMOTE_SERVICE_CLASS_NAME = "leakcanary.internal.RemoteLeakCanaryWorkerService"
// 這里通過(guò)RemoteLeakCanaryWorkerService這個(gè)類(lèi)是否加載成功來(lái)判斷
// 是否有'com.squareup.leakcanary:leakcanary-android-process:2.9.1'這個(gè)依賴(lài)
internal val remoteLeakCanaryServiceInClasspath by lazy {
try {
Class.forName(REMOTE_SERVICE_CLASS_NAME)
true
} catch (ignored: Throwable) {
false
}
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
val application = InternalLeakCanary.application
// 創(chuàng)建并分發(fā) WorkManager 多進(jìn)程請(qǐng)求
val heapAnalysisRequest =
OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
val dataBuilder = Data.Builder()
.putString(ARGUMENT_PACKAGE_NAME, application.packageName)
.putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
setInputData(event.asWorkerInputData(dataBuilder))
with(WorkManagerHeapAnalyzer) {
addExpeditedFlag()
}
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val workManager = WorkManager.getInstance(application)
workManager.enqueue(heapAnalysisRequest)
}
}
}
internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
RemoteListenableWorker(appContext, workerParams) {
override fun startRemoteWork(): ListenableFuture<Result> {
val heapDump = inputData.asEvent<HeapDump>()
val result = SettableFuture.create<Result>()
heapAnalyzerThreadHandler.post {
// 分析堆快照
val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
result.isCancelled
}) { progressEvent ->
if (!result.isCancelled) {
// 發(fā)送分析進(jìn)度事件
InternalLeakCanary.sendEvent(progressEvent)
}
}
if (result.isCancelled) {
SharkLog.d { "Remote heap analysis for ${heapDump.file} was canceled" }
} else {
// 發(fā)送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
result.set(Result.success())
}
}
return result
}
override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
return applicationContext.heapAnalysisForegroundInfoAsync()
}
}
2.5.2 WorkManager異步分析
object WorkManagerHeapAnalyzer : EventListener {
// 判斷是否包含WorkManager
internal val validWorkManagerInClasspath by lazy {
try {
Class.forName("androidx.work.WorkManager")
val dataBuilderClass = Class.forName("androidx.work.Data$Builder")
dataBuilderClass.declaredMethods.any { it.name == "putByteArray" }.apply {
if (!this) {
SharkLog.d { "Could not find androidx.work.Data$Builder.putByteArray, WorkManager should be at least 2.1.0." }
}
}
} catch (ignored: Throwable) {
false
}
}
// setExpedited() requires WorkManager 2.7.0+
private val workManagerSupportsExpeditedRequests by lazy {
try {
Class.forName("androidx.work.OutOfQuotaPolicy")
true
} catch (ignored: Throwable) {
false
}
}
internal fun OneTimeWorkRequest.Builder.addExpeditedFlag() = apply {
if (workManagerSupportsExpeditedRequests) {
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
setInputData(event.asWorkerInputData())
addExpeditedFlag()
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val application = InternalLeakCanary.application
WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
}
}
}
internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
// 分析堆快照
val doneEvent =
AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
// 發(fā)送分析進(jìn)度事件
InternalLeakCanary.sendEvent(event)
}
// 發(fā)送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
return Result.success()
}
...
}
2.5.3 普通異步線(xiàn)程分析BackgroundThreadHeapAnalyzer
object BackgroundThreadHeapAnalyzer : EventListener {
internal val heapAnalyzerThreadHandler by lazy {
val handlerThread = HandlerThread("HeapAnalyzer")
handlerThread.start()
Handler(handlerThread.looper)
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
heapAnalyzerThreadHandler.post {
// 分析堆快照
val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
// 發(fā)送分析進(jìn)度事件
InternalLeakCanary.sendEvent(event)
}
// 發(fā)送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
}
}
}
}
- 可以看到三種方式中最終LeakCanary 是通過(guò)子線(xiàn)程或者子進(jìn)程執(zhí)行
AndroidDebugHeapAnalyzer.runAnalysisBlocking
方法來(lái)分析堆快照的道盏,并在分析過(guò)程中和分析完成后發(fā)送回調(diào)事件而柑。 - 堆快照分析最終是交給 Shark 中的 HeapAnalizer 完成的,這里就不再展開(kāi)分析了捞奕,核心流程是:
- 1牺堰、在堆快照中尋找泄漏對(duì)象,默認(rèn)是尋找 KeyedWeakReference 類(lèi)型對(duì)象颅围;
- 2伟葫、分析 KeyedWeakReference 對(duì)象的最短引用鏈,并按照引用鏈簽名分組院促,按照 Application Leaks 和 Library Leaks 分類(lèi)筏养;
- 3斧抱、返回分析完成事件。
3. 總結(jié)
- 利用ContentProvider進(jìn)行初始化
- 注冊(cè) 5 種 Android 泄漏場(chǎng)景的監(jiān)控
- Activity是利用application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)監(jiān)聽(tīng)全局Activity生命周期
- Fragment首先是通過(guò)
Application.registerActivityLifecycleCallbacks(…)
接口監(jiān)聽(tīng) Activity.onCreate()事件渐溶,再通過(guò)FragmentManager.registerFragmentLifecycleCallbacks(…)
接口監(jiān)聽(tīng) Fragment 的生命周期 - ViewModel是通過(guò)
Hook
ViewModelStore的方式實(shí)現(xiàn)的 - Service是通過(guò)Hook
mH.mCallback
回調(diào)辉浦,監(jiān)聽(tīng)其中的STOP_SERVICE
消息來(lái)實(shí)現(xiàn)的 - RootView是通過(guò)借助curtains庫(kù)Hook WMS 服務(wù)內(nèi)部的
WindowManagerGlobal
實(shí)現(xiàn)
- 收到銷(xiāo)毀回調(diào)后,根據(jù)要回收對(duì)象創(chuàng)建 KeyedWeakReference 并關(guān)聯(lián) ReferenceQueue茎辐,延遲 5 秒檢查相關(guān)對(duì)象是否被回收宪郊,如果未被回收則開(kāi)啟服務(wù),dump heap 獲取內(nèi)存快照
.hprof
文件 - 通過(guò) Shark 庫(kù)解析
.hprof
文件拖陆,獲取泄漏對(duì)象弛槐,計(jì)算泄漏對(duì)象到 GC roots 的最短路徑 - 合并多個(gè)泄漏路徑并輸出分析結(jié)果,將結(jié)果展示到可視化界面