Android内存泄漏

本文详细阐述了Android内存泄漏的原理、后果、垃圾回收机制,重点讲解了Activity、Handler、线程和动画导致的内存泄漏,以及LeakCanary检测和hprof分析方法。提供了解决常见内存泄漏的策略和工具应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()
      }
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值