Android内存泄漏分析及检测工具LeakCanary简介,android平台架构的四个层次

return true

}

}

那么AppWatcherInstalleronCreate的方法是在何时调用的?其实就是ContentProvider是在何时初始化的。它其实是在ActivityThreadhandleBindApplication的方法中。

private void handleBindApplication(AppBindData data) {

// don’t bring up providers in restricted mode; they may depend on the

// app’s custom Application class

if (!data.restrictedBackupMode) {

if (!ArrayUtils.isEmpty(data.providers)) {

installContentProviders(app, data.providers);

}

}

// appl
ication 初始化

mInstrumentation.callApplicationOnCreate(app);

}

通过上述代码可知,ContentProvideronCreate方法要早于Application的初始化,而LeakCanary的初始化正是在ContentProvider初始化时进行的,也就是说它帮助我们自动执行了初始化的过程。

需要注意的是,使用这种方式进行初始化的时候,不可以执行耗时代码,因为ContentProvider的初始化也是在主线程进行了,否则就会导致app启动速度变慢。

LeakCanary检测原理
  • LeakCanary进行内存泄漏检测主要是以下几个步骤:
  1. 获取可能泄漏的对象

  2. 生成.hprof文件

  3. 分析.hprof文件,并进行提示

  • LeakCanary检测的对象
  1. Activity

  2. Fragment和ViewModel

  3. View

  4. Service

fun appDefaultWatchers(

application: Application,

reachabilityWatcher: ReachabilityWatcher = objectWatcher

): List {

return listOf(

ActivityWatcher(application, reachabilityWatcher),

FragmentAndViewModelWatcher(application, reachabilityWatcher),

RootViewWatcher(reachabilityWatcher),

ServiceWatcher(reachabilityWatcher)

)

}

首先我们来看一下ObjectWatcher ,它的关键代码如下:

@Synchronized fun watch(

watchedObject: Any,

description: String

) {

if (!isEnabled()) {

return

}

removeWeaklyReachableObjects()

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

checkRetainedExecutor.execute {

moveToRetained(key)

}

}

主要是对watchedObject使用了弱引用,同时注意到里面使用了ReferenceQueue,这两者结合使用可以实现如果弱引用关联的对象被回收,就会把这个弱引用加入到queue中,以此来判断该对象是否被回收。

LeakCanary主要的检测对象是以上4种,以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”

)

}

}

}

ActivityWatcher中注册了ActivityLifecycleCallbacks,同时在onActivityDestroyed的时候,执行了一些操作,查看源码:

@Synchronized override fun expectWeaklyReachable(

watchedObject: Any,

description: String

) {

if (!isEnabled()) {

return

}

removeWeaklyReachableObjects()

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

checkRetainedExecutor.execute {

moveToRetained(key)

}

}

上述代码的主要逻辑是:

  1. 移除弱可达的对象

  2. 将当前的watchedObject添加到KeyedWeakReference当中

  3. 将这个weakReference保存到数组中

  4. checkRetainedExecutor中执行moveToRetained方法

根据removeWeaklyReachableObjects方法中原理,如果这个对象除了由ObjectWatcher所添加的WeakReference以外,没有其他对象在引用它了,那么这个对象也就可以回收了,watchedObjects也就可以移除他了。

private fun removeWeaklyReachableObjects() {

var ref: KeyedWeakReference?

do {

ref = queue.poll() as KeyedWeakReference?

if (ref != null) {

watchedObjects.remove(ref.key)

}

} while (ref != null)

}

}

checkRetainedExecutor其实是个单例对象,里面会通过handler来延迟5s来执行方法。如果超过5s则会触发LeakCanary的泄漏检测机制。5s只是个经验值应该,因为GC并不是实时发生,因而预留5s交给GC操作。

触发了LeakCanary的泄漏检测之后,则会执行HeapDumpTriggerdumpHeap方法,在获取到了.hprof文件之后,调用HeapAnalyzerService.runAnalysis()给出分析结果。 关于.hprof文件的分析,不是本文重点,具体可以参考hprof文件协议。其分析基本也就是根据GC Root去寻找泄漏的对象,大体流程图如下。

在Android中常见的内存泄漏

单例

单例所导致的内存泄漏几乎是在android开发中最为常见的内存泄漏问题了。

public class Singleton {

private static Singleton singleton;

private Context context;

private Singleton(Context context) {

this.context = context;

}

public static Singleton getInstance(Context context) {

if (singleton == null) {

singleton = new Singleton(context);

}

return singleton;

}

}

在上面的代码中,如果在执行getInstance方法的时候,传入的是activity的对象,那么该activity对象就没法被及时回收,导致内存泄漏,可以考虑传入ApplicationContext,或者把context放入到方法变量中。

非静态内部类(包括匿名内部类)

非静态内部类会默认持有外部类的引用,如果它的生命周期长于外部类时,就会导致内存泄漏。 在android开发,这种情况常常见于Handler的使用。

尽可能避免使用静态变量

{

if (singleton == null) {

singleton = new Singleton(context);

}

return singleton;

}

}

在上面的代码中,如果在执行getInstance方法的时候,传入的是activity的对象,那么该activity对象就没法被及时回收,导致内存泄漏,可以考虑传入ApplicationContext,或者把context放入到方法变量中。

非静态内部类(包括匿名内部类)

非静态内部类会默认持有外部类的引用,如果它的生命周期长于外部类时,就会导致内存泄漏。 在android开发,这种情况常常见于Handler的使用。

尽可能避免使用静态变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值