| 引用类型 | GC 时机 |
|---|---|
| 强引用 | 平时写代码最常用的引用类型,对象只要被强引用就不会被 GC |
软引用 SoftReference |
只有当内存不足时才会被 GC |
弱引用 WeakReference |
会被正常 GC |
虚引用 PhantomReference |
会被正常 GC,因为 get() 总是返回 null,一般用来跟踪对象的生命周期 |
public abstract class Reference<T> {
/* -- Constructors -- */
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = queue;
}
}
所有的引用类型都可以在构造时与一个 ReferenceQueue 关联,当引用的对象被 GC 后,这个 Reference 将被入队到关联的引用队列里
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
// 持有被监控对象的弱引用,以便后续的检查
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
// 引用队列,被 GC 的对象的引用会进入此队列
private val queue = ReferenceQueue<Any>()
}
如何检测泄漏对象
ObjectWatcher 实现了 LeakCanary 的泄漏检测机制:监控 - 等待 - 检查
用 WeakReference + ReferenceQueue 监控对象的 GC 状态,并用 watchedObjects 持有它的弱引用,key 是 UUID
将指定对象交由 ObjectWatcher 进行监控
// 监控检测对象
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects() // 从 watchedObjects 移除已被 GC 的对象
// 弱引用监控对象并放入 watchedObjects
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
// 选机检查(默认 5s 后执行检查函数 moveToRetained)
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
// 出现在 queue 里的对象已被成功 GC 就不需要监控了
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)
}
检查是否发生泄漏,默认情况是等待 5s,让 GC 线程有足够的机会去发现并回收这个对象,如果 5s 后仍然没有被 GC(没有出现在引用队列里),那么可以证明这个对象发生了内存泄漏,被强引用导致存活超过它的生命周期
// 上面说过检查操作将提交给 checkRetainedExecutor 执行
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
// ...
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
// 而 checkRetainedExecutor 是通过构造函数传入的
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
private val isEnabled: () -> Boolean = { true }
)
// 默认是等待 5s 并在主线程执行检查操作
object AppWatcher {
private const val RETAINED_DELAY_NOT_SET = -1L
@Volatile private var retainedDelayMillis = RETAINED_DELAY_NOT_SET
val objectWatcher = ObjectWatcher(
clock = { SystemClock.uptimeMillis() },
checkRetainedExecutor = {
check(isInstalled) {
"AppWatcher not installed"
}
mainHandler.postDelayed(it, retainedDelayMillis) // 在主线程执行检查
},
isEnabled = { true }
)
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认等待 5s
watchersToInstall: List<InstallableWatcher> = 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
LeakCanaryDelegate.loadLeakCanary(application)
watchersToInstall.forEach {
it.install()
}
}
}
// 如果没有出现在引用队列里,说明此对象已发生泄漏,发出通知
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects() // 出现在引用队列里说明对象已被 GC,可以从 watchedObjects 移除
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
// 收集 destroyed Activity
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
检测 Activity 泄漏
通过 ActivityLifecycleCallbacks.onActivityDestroyed 可以收集到 destoryed Activity,这些 Activity 已走完它的生命周期,应该被后续的 GC 回收掉
internal class AndroidXFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
// 发现 View
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
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)"
)
}
}
// 发现 Fragment
override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
}
检测 Fragment 和 View 的泄漏
利用 FragmentLifecycleCallbacks 发现被 destroyed Fragment 和 View,然后用 ObjectWatcher 监控是否发生泄漏
internal class ViewModelClearedWatcher(
storeOwner: ViewModelStoreOwner,
private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
private val viewModelMap: Map<String, ViewModel>?
init {
// We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
// however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
// does not have ViewModelStore#keys. All versions currently have the mMap field.
viewModelMap = try {
val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
mMapField.isAccessible = true
@Suppress("UNCHECKED_CAST")
mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
} catch (ignored: Exception) {
null
}
}
override fun onCleared() {
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 <T : ViewModel?> create(modelClass: Class<T>): T =
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
})
provider.get(ViewModelClearedWatcher::class.java)
}
}
}
检测 ViewModel 泄漏
Fragment 里的 ViewModel 则是在 FragmentLifecycleCallbacks.onFragmentCreated 时,注入一个 ViewModel,通过反射拿到 ViewModelStore.mMap,这里有所有的 ViewModel,在 ViewModel.onCleared 时把它们加入 ObjectWatcher 进行泄漏检查
class MyService : Service {
// ...
override fun onDestroy() {
super.onDestroy()
AppWatcher.objectWatcher.watch(
watchedObject = this,
description = "MyService received Service#onDestroy() callback"
)
}
}
检测更多类型的泄漏
对 Service 的检查就比较 hack 了,通过反射替换 ActivityThread.mH.mCallback,通过 Message.waht == H.STOP_SERVICE 定位到 ActivityThread.handleStopService 的调用时机,然后把这个被 stop 的 Service 记录下来;用动态代理实现 IActivityManager 并替换掉 ActivityManager.IActivityManagerSinglteon.mInstance,从而拦截方法 serviceDoneExecuting,此方法的调用表示 Service 生命周期已完结,可以把它交由 ObjectWatcher 进行监控
这给我启示,对于我们感兴趣的对象(需要警惕泄漏的对象,比如 Bitmap),都可以通过 ObjectWatcher 去检测泄漏问题
发现对象泄漏后触发 OnObjectRetainedListener.onObjectRetained(),最终调用 Debug.dumpHprofData 生成 hprof 文件
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
}
internal class HeapDumpTrigger {
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0) {
return
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects()
}, delayMillis)
}
private fun checkRetainedObjects() {
// ...
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean,
reason: String
) {
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
when (val heapDumpResult = heapDumper.dumpHeap()) {
// ...
}
}
}
internal class AndroidHeapDumper {
override fun dumpHeap(): DumpHeapResult {
// ...
val durationMillis = measureDurationMillis {
Debug.dumpHprofData(heapDumpFile.absolutePath)
}
// ...
}
}
解析 hprof 文件 - hprof 文件格式



hprof 是结构紧凑的二进制文件,整体上分为 Header 和 Record 数组两大部分
Header 总共 18 + 4 + 4 + 4 = 32 字节,包括:

Record 数组记录了内存中的各种数据
Record 的类型(1 字节)Record BODY 的长度(4 字节)Record 类型有不同的 BODY支持的 TAG 类型主要有: