leakcanary原理分析与AppsFly内存泄漏

Mat内存分析方法

首先大致介绍一下用mat分析内存的方法

  • 在我们怀疑有内存泄漏的时候,dump heap file.(比如退出某一个activity的时候,怀疑activity未被回收)

    dump heap

    此时我们拿到的是一个android studio上能识别的hprof 文件,如下图

    android studio

  • 目前android studio上分析内存功能有限,一般使用eclipse mat。
    可以使用android/platform-tools目录下的工具hprof-conv 进行转换
    如 hprof-conv android.hprof mat.hprof

  • 点击这个tab后,可以输入类名进行搜索,如下图

    搜索

    搜索到类后,我们需要看看是否有强引用,也就是除弱引用与软引用外的reference,如下图

    最短路径

    最终发现有两个强引用,也就是退出activity后,出现了内存泄漏,如下图

    这里写图片描述

    经检查,发现ConnectivityManager中sInstance hold了 StartupActivity,其中SystemServiceRegistry
    也是用于获取系统Manager的类实际上是调用SystemServiceRegistry中getService方法。因此推断出,是获取ConnectivityManager时,传递了StartupActivity。

leakcanary原理分析

leakcanary是一个帮我们分析内存泄漏的工具,非常方便
源码和使用说明见github:
https://github.com/square/leakcanary

现在有了leakcanary,我们能方便的监控activity的内存泄漏了。例如上面的内存泄漏,leakcanary也能帮我们捕获到,如下图:

这里写图片描述

如果要了解详细的泄漏信息,可以点标题栏上的menu,将 dump file发送到电脑,然后利用mat进行上面的分析。

leakcanary 是如何监控activity内存泄漏的呢?主要原理是watch一个即将要销毁的对象,例如这个对象是activity的话,那么在Activity的onDestroy之后,leakcanary将会监控这个activity。

这里注意一下,leakcanary只在application onCreate时调用了LeakCanary.install(this);它是怎么监控activity的呢?主要靠的 application.registerActivityLifecycleCallbacks(listener),app中的每个activity的每一个生命周期都会回调listener中相关方法,从而leakcanary在生命周期相关方法中能取到Activity。

leakcanary 的watch流程是在主线程空闲时,delay 5秒后检查对象有没有被回收。其中空闲时执行check是用的IdleHandler。判断一个对象有没有被回收是用的WeakReference与ReferenceQuene, 如使用WeakReference(T referent, ReferenceQueue queue) 构造方法构造WeakReference时,如果这个WeakReference连接的对象被回收的话,系统会将WeakReference put进 ReferenceQuene,因此,如果ReferenceQuene中有哪个WeakReference,则表示这个WeakReference弱链接的对象已被回收。

如leakcanary 源码中watch对象代码如下:

这里写图片描述

空闲时check是否被回收代码如下:

这里写图片描述

判断内存是否被回收,没有被回收GC一次后再判断是否被回收,任然没有被回收则会通过Debug.dumpHprofData() 生成heap file,然后在service中通过haha(com.squareup.haha) 开源工具类进行文件分析,分析到强应用最短路径后,发送到UI线程弹出对话框。部分代码如下:
create profile

AppsFly内存泄漏

AppsFly是海外最流行的App安装与事件追踪最流行的平台之一,主要是被许多主流广告平台所认可。
官网: https://support.appsflyer.com/

由于分析之前的内存泄漏发现,除了增加了一个AppsFly初始化的操作外,并没有创建ConnectivityManager,而leakcanary 和 mat分析都表明有这个内存泄漏,因此计划了解一下AppsFly到底做了哪些操作。
初始化代码如下:

private void initAppsFlyerSDK() {
        AppsFlyerLib.getInstance().setCollectAndroidID(false);
        AppsFlyerLib.getInstance().setCollectIMEI(false);
        AppsFlyerLib.getInstance().setDebugLog(AppEnvConfig.DEBUG);
        AppsFlyerLib.getInstance().setAndroidIdData(AppEnvUtils.getAndroidID());
        AppsFlyerLib.getInstance().setImeiData(AppEnvUtils.getIMEI(this.getApplicationContext()));
        AppsFlyerLib.getInstance().startTracking(getApplication(), APPSFLYER_DEV_KEY);
}
显然没有传activity给AppsFly,继续进入AppsFlyerLib,直接搜索ConnectivityManager,找到了相关代码:
![这里写图片描述](https://img-blog.csdn.net/20160824003509606)

继续查找getNetwork调用堆栈,如下图:
这里写图片描述

第1点不会出现内存泄漏,第2点,由于将Activity当Context传递给getNetwork方法。导致获取ConectivityManager后,系统没有及时释放activity。

这里注意一下,AppsFly初始化时也没有传activity,而查代码发现AppsFly也是通过注册生命周期回调application.registerActivityLifecycleCallbacks(listener),在回调中处理各个Activity的统计。

解决办法:由于问题原因是getSystemService是调用的Activity的getSystemService,而不是ApplicationContext导致的内存泄漏,因此我们可以在BaseActivity中重写getSystemService方法。除了LayoutInflater是有可能需要Activity上下文去加载View的,获取其他SystermManager都可以用ApplicationContext. 修改后测试,再没遇到Appsfly的内存泄漏

    @Override
    public Object getSystemService(@NonNull String name) {
        if (AppEnv.DEBUG) {
            OLog.d(getClass().getSimpleName(), "getSystemService name:" + name);
        }

        if (!Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
            return getApplicationContext().getSystemService(name);
        }
        return super.getSystemService(name);
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值