Android 内存泄漏
短生命周期的对象被长生命周期的对象持有,从而导致短生命周期的对象不能被释放。
内存泄漏的后果
内存溢出(OutOfMemoryError),Android 对每个应用分配的堆大小设置了限制,当应用程序使用的内存达到了该限制尝试分配更多内存时,系统就会抛出 OutOfMemoryError .
内存泄漏(Memory Leak),应用在申请内存后,无法释放申请的内存空间,当内存泄漏越来越多,最坏的情况下就会导致内存溢出。
垃圾回收机制
分为 引用计数法 和 可达性分析 。
引用计数法
使用的语言:Python、Object-C、Swift .
用一个计数器记录一个对象被引用的次数,当引用的次数减少到 0 时,就说明该对象是一个垃圾对象,可以被回收。
引用计数有互相引用的问题。
可达性分析法
使用的语言:Java .
JVM 通过 GC Roots 向下搜索,如果可以被 GC Roots 引用到的对象,说明这个对象不是垃圾对象,反之这个对象就是互相引用了也是垃圾对象。
哪些对象可以作为 GC Roots
- 在线程栈中的局部变量,也就是正在被调用的方法,它里面的参数和局部变量
- 存活的线程对象
- Class 对象,Android 在 加载 Class 后不会卸载 Class
- 引用类型的静态变量
- 本地方法栈中 JNI(Native)引用的对象
常见的内存泄漏
Activity 内部类泄漏
内部类默认直接持有这个 Activity 的引用,如果内部类的生命周期比 Activity 生命周期长,那么这个 Activity 在销毁的时候,由于内部类持有 Activity 的引用,导致 Activity 在 GC 时无法被回收,造成内存泄漏。
Activity 内部 Handler
在 Activity 内部使用非静态 Handler ,那么该 Handler 对象就持有 Activity 的引用,如果在 Activity 退出后,还有延时任务没有执行,那么 Activity 是无法被回收的。
可以通过自定义 Handler,使用弱引用(WeakReference )存储 Activity,在 Activity 内部使用静态的 自定义 Handler 。静态类在虚拟机加载的时候是单独加载到内存中的,因为是持有的是 Activity 的弱引用,在 GC 的时候,Activity 对象是可以被回收的。另外最好在 Activity 的 onDestory 方法中 移除 Handler 的所有消息。
Activity 内部线程
在 Activity 中使用线程执行异步任务,如果内部线程的生命周期比 Activity 生命周期长,由于内部线程持有 Activity 的引用,导致 Activity 无法被回收。在这个线程执行完毕后,Activity 可以被正常回收,但是会造成一个崩溃的风险,可能在线程内部用到一些 Activity 的内部对象,在 Activity 退出后,这些对象可能被回收了。
动画导致的内存泄漏
在 Activity 中如果有一些控件绑定一起的属性动画在运行,Activity 退出的时候需要 cancel 掉这些动画。如果不取消掉这些属性动画,那么就会一直去执行控件的 onDraw 方法,由于 控件持有 Activity 的引用,而属性动画持有了控件,那么 Activity 对象也就不能被释放。
属性动画对象尽量不要用 static 修饰,因为 ValueAnimator 是静态的,添加了动画属性改变的监听 addUpdateListener,在回调监听里面持有 View 控件,默认就持有了 Activity 对象。如果必须使用 static 修饰,可以在动画结束时,使用 ValueAnimator .removeAllUpdateListeners 移除监听。
传 Context 使用 Activity 造成内存泄漏
如果单例需要传 Context 对象,在单例中如果把 Context 保存起来,那么这个单例一旦创建就一直存在了,如果传入的是 Activity,那么单例将一直持有 Activity 对象引用导致内存泄漏。
资源未关闭的内存泄漏
资源性对象(如InputStream/OutputStream,Cursor,File文件等)未及时 close,也会导致 GC 无法回收这部分内存。
引用类型
强运用(StrongReference): 在 GC 时,不会被回收
软引用(SoftReference): 内存不足时,如果一个对象只有软引用,会被回收
弱引用(WeakReference): 在 GC 时,如果一个对象只有弱引用,会被回收
虚引用(PhantomReference): 任何时候都有可能被回收
内存泄漏检测
LeakCanary 是一个 Android 内存泄漏检测库。
LeakCanary 使用
dependencies {
// 只有在编译debug包时,才会将leakcanary的代码打入包内
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}
在应用启动后,看到 LeakCanary 输出以下的日志表示 LeakCanary 正在运行:
D LeakCanary: LeakCanary is running and ready to detect leaks
LeakCanary 源码分析
LeakCanary 利用 ContentProvider 在 Application 创建后就创建的原理,在 ContentProvider 中完成 LeakCanary 的初始化。通过对 ActivityWatcher、FragmentAndViewModelWatcher 等在对象销毁时将引用传递给 ObjectWatcher,由 ObjectWatcher 判断对象是否可能存在泄漏。
hprof 文件
hprof 即 heap profile 文件,表示当前堆内存快照,通过分析 hprof 文件,可以知道哪些对象存在内存泄漏。
获取 hprof 文件
// 下面代码会暂时挂起所有线程,并将当前堆快照保存到指定的fileName文件
Debug.dumpHprofData(fileName)
LeakCanary 检测内存泄漏
自动检测 Activity、Fragment 的销毁,判断是否存在内存泄漏;在发生内存泄漏的时候,捕捉内存快照 hprof 文件,在 HeapAnalyzerService 使用 Shark 分析 hprof 文件。
MainProcessAppWatcherInstaller
LeakCanary 实现自动检测的 ContentProvider 是 MainProcessAppWatcherInstaller,可在编译好的应用中的清单文件中查看。
<provider
android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
android:enabled="@ref/0x7f040006"
android:exported="false"
android:authorities="com.chen.leak.leakcanary-installer" />
// MainProcessAppWatcherInstaller 类
internal class MainProcessAppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)// 调用 manualInstall 方法
return true
}
...
}
// AppWatcher 类
object AppWatcher {
...
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
...
LeakCanaryDelegate.loadLeakCanary(application)// 当有内存泄漏触发回调
watchersToInstall.forEach {
it.install()
}
}
}
// 默认的Watcher,有四类:ActivityWatcher、FragmentAndViewModelWatcher、RootViewWatcher、ServiceWatcher
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
}
ActivityWatcher
初始化时通过注册 ActivityLifecycleCallbacks 实现 Activity 销毁时收到回调。收到回调后将引用传递给 ObjectWatcher,ObjectWatcher 持有该 Activity 的弱引用,并在 5 秒后分析是否回收。
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"
)
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
ObjectWatcher
ObjectWatcher 只能检测当前可能存在内存泄漏,在 HeapDumpTrigger 会在工作线程再次校验一次是否存在内存泄漏,触发一次 GC,然后检查是否有对象未被 GC 标记回收,如果未被标记则发生了内存泄漏,接着进行 dump deap,接着调用 HeapAnalyzerService 对 hprof 文件做进一步分析。
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
// 当检测对象发生内存泄漏的监听
private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()
// 弱引用保存当前被观察的对象
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
// 所有弱引用对象注册到的ReferenceQueue,用来判断弱引用所指向的对象是否被回收
private val queue = ReferenceQueue<Any>()
...
// 当 Activity 销毁后,以弱引用持有其引用,用于后续判断是否被回收
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
removeWeaklyReachableObjects()//
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)// 弱引用持有
watchedObjects[key] = reference// 放到观察列表中
checkRetainedExecutor.execute {// 间隔5s 在主线程判断对象是否被回收
moveToRetained(key)
}
}
// 在当前观察的对象列表中,将已经被回收的对象移除,剩余的其他对象就是可能发生泄漏的对象
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }// 回调可能发生内存泄漏的对象
}
}
// 移除已被 gc 回收的观察对象
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
LeakCanaryDelegate
LeakCanaryDelegate.loadLeakCanary 会触发 ObjectWatcher.addOnObjectRetainedListener 监听的添加。
// LeakCanaryDelegate 类
internal object LeakCanaryDelegate {
@Suppress("UNCHECKED_CAST")
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
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
...
override fun invoke(application: Application) {
_application = application
checkRunningInDebuggableBuild()
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)// 监听的添加
val gcTrigger = GcTrigger.Default// gc的实现
val configProvider = { LeakCanary.config }
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
// deapDump 触发器,在工作线程
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
configProvider
)
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
registerResumedActivityListener(application)
addDynamicShortcut(application)// 添加桌面快捷方式
mainHandler.post {
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()
)
}
}
}
}
}
// 收到存在内存泄漏的回调后
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
}
// HeapDumpTrigger 类
internal class HeapDumpTrigger(
private val application: Application,
private val backgroundHandler: Handler,
private val objectWatcher: ObjectWatcher,
private val gcTrigger: GcTrigger,
private val configProvider: () -> Config
) {
// 在工作线程再次校验
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() {
...
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()// 触发 gc
retainedReferenceCount = objectWatcher.retainedObjectCount
}
val visibility = if (applicationVisible) "visible" else "not visible"
// 执行 dump heap
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
}
GcTrigger
垃圾回收触发器
fun interface GcTrigger {
fun runGc()
object Default : GcTrigger {
override fun runGc() {
Runtime.getRuntime()
.gc()
enqueueReferences()
System.runFinalization()
}
private fun enqueueReferences() {
try {
Thread.sleep(100)
} catch (e: InterruptedException) {
throw AssertionError()
}
}
}
}