首页 > 系统相关 >Android:性能优化工具之内存泄露-LeakCanary

Android:性能优化工具之内存泄露-LeakCanary

时间:2022-11-29 18:34:18浏览次数:76  
标签:泄漏 LeakCanary 引用 context RefWatcher Android 内存


目录

​​一 简介​​

​​二 使用​​

​​三 进阶用法​​

​​四 hprof分析复杂内存泄露问题​​

​​五 使用小结​​

​​六 使用踩坑​​

​​6.1 权限​​

​​6.2  NullPointerException​​

​​七 原理简单介绍​​

​​7.1 触发检测​​

​​7.2 判断是否存在内存泄漏​​

​​7.3 分析内存泄漏​​

​​八 总结​​


参考源码地址:​​https://github.com/LucasXu01/Autils​

一 简介

使用MAT来分析内存问题,有一些门槛,会有一些难度,并且效率也不是很高,对于一个内存泄漏问题,可能要进行多次排查和对比才能找到问题原因。 为了能够简单迅速的发现内存泄漏,Square公司基于MAT开源了LeakCanary ;

github地址:​​https://github.com/square/leakcanary​

官方文档:​​https://square.github.io/leakcanary/​


二 使用

在app build.gradle 中加入引用:

dependencies {
//内存泄露
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'

}

在 Application 中:

public class MyApplication extends Application {
@Override public void onCreate() {
super.onCreate();

/** 基础使用 */
if (LeakCanary.isInAnalyzerProcess(this)) {//1
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);


}
}

如果当前的进程是用来给LeakCanary 进行堆分析的则return,否则会执行LeakCanary的install方法。这样我们就可以使用LeakCanary了,如果检测到某个Activity 有内存泄露,LeakCanary 就会给出提示。

Android:性能优化工具之内存泄露-LeakCanary_Androiod

 


三 进阶用法

例子代码只能够检测Activity的内存泄漏,当然还存在其他类的内存泄漏,这时我们就需要使用RefWatcher来进行监控。

改写Application,如下所示:

public class MyApplication extends Application {
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher= setupLeakCanary();
}

private RefWatcher setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
}
return LeakCanary.install(this);
}

public static RefWatcher getRefWatcher(Context context) {
MyApplication leakApplication = (MyApplication) context.getApplicationContext();
return leakApplication.refWatcher;
}
}

install方法会返回RefWatcher用来监控对象,LeakApplication中还要提供getRefWatcher静态方法来返回全局RefWatcher。

最后为了举例,我们在一段存在内存泄漏的代码中引入LeakCanary监控,如下所示。

泄露所在的活动:

/**
* 内存泄露
* @author lucas
* created at 2019/9/22 2:53 PM
*/
public class LeakActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak_test);

LeakTestManager manager = LeakTestManager.getInstance(LeakActivity.this);
}

外部引用:

public class LeakTestManager {

private static LeakTestManager manager;
private Context context;

private LeakTestManager(Context context) {
this.context = context;
}

/**
* 如果传入的context是activity/service的上下文,会导致内存泄漏
* 原因是我们的manger是一个static的静态对象,这个对象的生命周期和整个app的生命周期一样长
* 当activity销毁的时候,我们的这个manger仍然持有者这个activity的context,就会导致activity对象无法被释放回收,就导致了内存泄漏
*/

public static LeakTestManager getInstance(Context context) {
if (manager == null) {
manager = new LeakTestManager(context);
}
return manager;
}

//正确写法
public static LeakTestManager getInstanceSafe(Context context) {
if (manager == null) {
manager = new LeakTestManager(context.getApplicationContext());
}
return manager;
}

}

LeakActivity存在内存泄漏,原因就是非静态内部类LeakTestManager持有外部类LeakActivity的引用,LeakTestmanger是一个static的静态对象,这个对象的生命周期和整个app的生命周期一样长,当activity销毁的时候,我们的这个manger仍然持有者这个activity的context,就会导致activity对象无法被释放回收,就导致了内存泄漏。

***LeakCanary会自动监控Activity执行onDestroy方法之后是否发生内存泄露,当前此例onDestroy加是多余的,这里只是为了方便举例,如果想要监控Fragment,则必须在Fragment中添加如上的onDestroy方法!

运行程序,打开泄露的活动界面,需要稍等片刻,打开LeakCanary内存泄漏信息就会展示出来(原生安卓会直接Notification,深度定制过的类似小米不会弹出弹窗,因不同版本而异):

Android:性能优化工具之内存泄露-LeakCanary_内存泄露_02

 

Android:性能优化工具之内存泄露-LeakCanary_安卓_03


四 hprof分析复杂内存泄露问题

上面内存泄漏的例子比较简单,可以很明显的看出泄漏的原因,有时候我们会遇到比较复杂的内存泄漏情况,这个时候我们可能需要分析一下hprof文件。
打开Device File explorer,找到安卓中的Download文件夹:

Android:性能优化工具之内存泄露-LeakCanary_内存泄露_04

双击hprof文件,再进行分析:

Android:性能优化工具之内存泄露-LeakCanary_Androiod_05

hprof文件里信息很多,其中常用字段的含义:

名称

描述

Class name

类名

Total Count

该类的实例总数

Heap Count

所选择的堆中该类的实例的数量

Sizeof

单个实例所占空间大小(如果每个实例所占空间大小不一样则显示0)

Shallow Size

堆里所有实例大小总和(Heap Count * Sizeof)

Retained Size

该类所有实例所支配的内存大小

Instance

具体的实例

Reference Tree

所选实例的引用,以及指向该引用的引用。

Depth

GC根节点到所选实例的最短路径的深度

Shallow Size

所选实例的大小

Dominating Size

所选实例所支配的内存大小


五 使用小结

如果只关注activity的内存泄漏,那么在Application中onCreate加入LeakCanary.install(this);就OK了,

如果还关注fragment的泄漏情况,那么Application加上RefWatcher,然后在对应fragment页面中onDestroy中加入:

RefWatcher refWatcher = MyApplication.getRefWatcher(this);
refWatcher.watch(this);

六 使用踩坑

在刚开始使用LeakCanary的时候,遇到了几个问题导致有内存泄漏发生时LeakCanary不发生通知,这里和大家分享一下。

6.1 权限

你的应用需要有写SD权限,因为LeakCanary需要生成hprof文件,保存在SD卡里面,因此你的应用要先申请权限。但在本文中的1.6.3版本中,你只需要在manifest清单中添加下面两条权限即可,需要时LeakCanary会自动申请零时零时权限,不需要自己再在代码中手动控制了。

<!– SDCard中创建与删除文件权限 –>
<uses-permission android:name=“android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>

<!– 向SDCard写入数据权限 –>
<uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/>

6.2  NullPointerException

java.lang.NullPointerException: Attempt to invoke virtual method’boolean java.lang.String.equals(java.lang.Object)’ on a null object reference

atcom.squareup.leakcanary.HeapAnalyzer.findLeakingReference(HeapAnalyzer.java:160)

….

如果遇到这个问题,是LeakCanary的版本过低了,不适合Android6.0及以上的机型,我看网上大部分引用的还是基于1.3的版本,升级到高版本的就没问题了。


七 原理简单介绍

7.1 触发检测

每次当Activity/Fragment执行完onDestroy生命周期,LeakCanary就会获取到这个Activity/Fragment,然后初始化RefWatcher对它进行分析,查看是否存在内存泄漏。

7.2 判断是否存在内存泄漏

首先尝试着从ReferenceQueue队列中获取待分析对象(软引用和弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用或弱引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用或弱引用加入到与之关联的引用队列中),如果不为空,那么说明正在被系统回收,如果直接就返回DONE,说明已经被系统回收了,如果没有被系统回收,可能存在内存泄漏,手动触发系统GC,然后再尝试移除待分析对象,如果还存在,说明存在内存泄漏。

没有引用了,就去ReferenceQueue中找,没有就是泄露了。

7.3 分析内存泄漏

确定有内存泄漏后,调用heapDumper.dumpHeap()生成.hprof文件目录。HAHA 是一个由 square 开源的 Android 堆分析库,分析 hprof 文件生成Snapshot对象。Snapshot用以查询对象的最短引用链。找到最短引用链后,定位问题,排查代码将会事半功倍。

Android:性能优化工具之内存泄露-LeakCanary_安卓_06

整体流程如下:

Android:性能优化工具之内存泄露-LeakCanary_内存泄露_07

详细原理分析可以参考:​​LeakCanary原理分析​​


八 总结

LeakCanary对于内存泄漏的检测非常有效,但也并不是所有的内存泄漏都能检测出来。

6.1 无法检测出Service中的内存泄漏问题

6.2 如果最底层的MainActivity一直未走onDestroy生命周期(它在Activity栈的最底层),无法检测出它的调用栈的内存泄漏。

所以说LeakCanary针对Activity/Fragment的内存泄漏检测非常好用,但是对于以上检测不到的情况,还得配合Android Monitor + MAT 来分析。

 


标签:泄漏,LeakCanary,引用,context,RefWatcher,Android,内存
From: https://blog.51cto.com/u_12853553/5896524

相关文章

  • Android 调试桥:adb的入门与最佳实践(无线连接调试)
    商业转载请联系作者获得授权,非商业转载请注明出处。目录​​一adb简介​​​​二 adb工作原理​​​​三配置adb环境​​​​四常用的adb命令​​​​4.1help命令​​......
  • Android手把手,发布开源组件至 MavenCentral仓库
    一前言有时候,在我们写了一个组件想将之开源给更多人分享和使用时,就需要我们发布开源组件到公开的远程仓库,如Jitpack、JenCenter、MavenCentral。其中,MavenCentral是最......
  • Android:按下与提起的状态background
    <?xmlversion="1.0"encoding="utf-8"?><selectorxmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:drawable="@color/btn_pressed_green_......
  • 连浏览器都卷,64g小内存手机配不上夸克的光环了
    说实话,夸克真的好用啊,除了占内存没啥毛病。之前手机上也是用的夸克浏览器,用着用着发现它的体积越来越大了,我那64g小内存手机配不上夸克的光环了。于是,只能默默地卸掉了夸克......
  • 使用LRU算法缓存图片,android 3.0
    在您的UI中显示单个图片是非常简单的,如果您需要一次显示很多图片就有点复杂了。在很多情况下(例如使用ListView,GridView或者 ​​​ViewPager​​​控件),显示在屏幕......
  • android viewgroup事件分发机制
    1、案例首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout:[java]​​viewplain​​​​copy​​package  importimportimportimportimpor......
  • android混淆
    为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤:1.大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt......
  • android自定义view实现progressbar的效果
    一键清理是很多Launcher都会带有的功能,其效果也比较美观。实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:​​模仿......
  • android studio添加project libs库步骤
    在Eclipse中选择要导出的项目,然后依次选择菜单file->export->Android->GenerateGradlebuild files.之后依次点击next到finish即可回到Androidstudio打开settin......
  • android守护进程
    Service组件在android开发中经常遇到,其经常作为后台服务,需要始终保持运行,负责处理一些必要(见不得人)的任务。而一些安全软件,如360等,会有结束进程的功能,如果不做Service的保持......