天天看点

Android 如何做一次内存泄漏大排查

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/112335970

本文出自【赵彦军的博客】

文章目录

    • 前言
    • 把内存泄漏的地方找出来
    • leakcanary 状态
    • hprof
      • 如何获取 HPROF
      • 如何打开 hprof 文件
    • Heap Dump
      • Heap Dump 是什么?
      • Heap Dump里面有什么?
    • 如何做一次 `Heap Dump` ?
    • 如何代码触发 Heap Dump
    • 如何手动触发 GC
    • Android Profiler指标
    • Heap Dump指标分析
    • 对象跟踪策略
    • 记录某一段时间的内存分配情况
    • Memory Analyzer(MAT)
    • 祝福

前言

眼瞅着还有一个月就过年,项目也没有那么忙了,技术老大要求做一做性能优化方面的工作。

而我的任务就是把项目中的内存泄漏撸一遍,然后安排对应的人处理。

说到内存泄漏,我也算是老手了,其实在

2016

年我就写个内存泄漏方面的文章:

Android 如何有效的解决内存泄漏的问题

把内存泄漏的地方找出来

说干就干,首要的任务就是把内存泄漏的代码揪出来,我选择

leakcanary

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

关于 leakcanary 的介绍,集成步骤,我就不在这里展开讲述了,网上有很多文章,你们自己搜索一下。

我只啰嗦一点:

  • 1、

    leakcanary

    2.0之后的版本集成不需要初始化

集成完成后,把项目跑起来,过一会就会报出来很多内存泄漏的日志。

在 Android Studio

logcat

过滤

LeakCanary

就会看到如下:

Android 如何做一次内存泄漏大排查

同时在手机桌面也会有一个 小鸟图标,点击会看到可视化页面

Android 如何做一次内存泄漏大排查

这里就可以看到内存泄漏的链条,分析的方式是

从下往上

的顺序,比如针对本图:

首先 MainActivity 实例发生内存泄漏

-->

再往上可以看到

MainActivity

泄漏 原因是 MainActivity 里的一个 Lambda 表达式引起的

-->

-->

再往上 看到

MutableLiveData

引起的

-->

-->

看到这个分析链条,我们就很清楚了,大概率是

MutableLiveData

对象引起的,再结合实际的项目代码,最后发现果然是因为

MainActivity

里的

MutableLiveData

对象没有释放。

特别要注意的是:

纵然

leakcanary

工具很牛逼,但是要想清晰的定位,然后修复内存泄漏,还是要结合实际的项目代码的。

到这里我们基本就完成了 把内存泄漏揪出来 的问题。

leakcanary 状态

在上面一部分,我贴了两个图,图中的有很清晰的对象引用链条。leakcanary 对每个对象都标明了泄漏的状态。

  • Leaking: YES 确定已经泄漏
  • Leaking: UNKNOWN 不确定是否泄漏
  • Leaking: NO 没有泄漏

我们在分析 对象引用链条的时候,要特别注意

UNKNOWN

状态,这个状态即有可能是泄漏了,也有可能是没有泄漏,这就需要我们程序要认真的分析项目代码,然后给出结论

hprof

leakcanary 在运行的时候,发现内存泄漏了,会把 Java堆快照转储到Android HPROF文件中,方便开发者分析。

如何获取 HPROF

方式一:通过 logcat 获取

在 Android Studio 的 logcat 中会输出 hprof 文件地址:

Android 如何做一次内存泄漏大排查

我们拿到 hprof 文件链接后,就可以通过

adb pull

命令导到电脑桌面

adb pull /storage/emulated/0/Download/leakcanary-com.cootek.crazyreader/2021-01-08_11-21-08_472.hprof ~/DeskTop

方式二:通过客户端可视化页面

Android 如何做一次内存泄漏大排查

点击

Share Heap Dump file

可以通过分享出去。

正文到这里其实也就结束了。在做内存泄漏排查的时候用到了 AndroidStudio Profiler 工具,里面有很多新的概念和内存指标,下面的内容就是在探究 Profiler 工具如何使用以及各种内存指标所代表的含义

如何打开 hprof 文件

方式一:

Android studio

Profiler 功能打开

Android 如何做一次内存泄漏大排查

这个工具显示了如下信息:

名称 描述
Class name 类名
Total Count 该类的实例总数
Heap Count 所选择的堆中该类的实例的数量
Sizeof 单个实例所占空间大小(如果每个实例所占空间大小不一样则显示0)
Shallow Size 堆里所有实例大小总和(Heap Count * Sizeof)
Retained Size 该类所有实例所支配的内存大小
Instance 具体的实例
Reference Tree 所选实例的引用,以及指向该引用的引用。
Depth GC根节点到所选实例的最短路径的深度
Shallow Size 所选实例的大小
Dominating Size 所选实例所支配的内存大小

用HPROF分析工具,可以检测到泄漏的 Activity

Android 如何做一次内存泄漏大排查

通过这个可以看到 本次分析有 3 出泄漏的地方,点击 第一个 ReaderActivity

Android 如何做一次内存泄漏大排查

可以看到详细的泄漏实例,Depth 为 13 ,代表 GC根节点到所选ReaderActivity实例的最短路径的深度是 13 。

Heap Dump

Heap Dump 是什么?

Heap Dump

也叫堆转储文件,是一个

Java

进程在某个时间点上的内存快照。

Heap Dump

是有着多种类型的。不过总体上

heap dump

在触发快照的时候都保存了

java

对象和类的信息。通常在写

heap dump

文件前会触发一次

FullGC

,所以

heap dump

文件中保存的是

FullGC

后留下的对象信息。

简单说就是:

heap dump

文件是一个二进制文件,它保存了某一时刻

JVM

堆中对象使用情况。

HeapDump

文件是指定时刻的

Java

堆栈的快照,是一种镜像文件。

Heap Dump里面有什么?

一般在

Heap Dump

文件中可以获取到(这仍然取决于heap dump文件的类型)如下信息:

  • 对象信息:类、成员变量、引用值;
  • 类信息:类加载器、名称、超类、静态成员;
  • Garbage Collections Roots:JVM可达的对象;
  • 线程栈以及本地变量:获取快照时的线程栈信息,以及局部变量的详细信息

也就是说我们可以对上面这些内容进行分析。通常可以基于

Heap Dump

分析如下类型的问题:

  • 找出内存泄漏的原因;
  • 找出重复引用的jar或类;
  • 分析集合的使用;
  • 分析类加载器。

总而言之我们对

Heap Dump

的分析就是对应用的内存使用进行分析,从而更加合理地使用内存。

如何做一次

Heap Dump

在前面讲到的,

hprof

文件都是

Leakcanary

工具帮我们做的,那我们自己想要自己做一次

Heap Dump

,生成 hprof 文件又该怎么做呢?

其实 AndroidStudio 有现成的工具,只要动动手机就行了。

AndroidStudio

-->

Profiler

-->

点击 + 号

-->

选择设备

-->

选择进程

-->

点击 MEMORY

-->

点击 向下的箭头

Android 如何做一次内存泄漏大排查

由于生成的 hprof 文件比较大,所以解析出来比较慢,要耐心等待。

Android 如何做一次内存泄漏大排查

至此,我们就完成手动

Heap Dump

操作,并且生成

hprof

文件 。我们也可以点击保存按钮,把

hprof

文件保存到桌面,或者发给其他人。

Android 如何做一次内存泄漏大排查

如何代码触发 Heap Dump

代码其实很简单:

try {
     //指定Hprof文件的名字
     var path: String =
                externalCacheDir?.absolutePath + File.separator + System.currentTimeMillis() + ".hprof"
     Debug.dumpHprofData(path)
} catch (e: Exception) {

}
           

生成的文件在

Android/data/app包名/cache/

目录下:

Android 如何做一次内存泄漏大排查

如何手动触发 GC

AndroidStudio

-->

Profiler

-->

点击 + 号

-->

选择设备

-->

选择进程

-->

点击 MEMORY

-->

点击 像垃圾桶 的图标

Android 如何做一次内存泄漏大排查

其实最快速的是点击 右键

Android 如何做一次内存泄漏大排查

Android Profiler指标

官方文档:https://developer.android.com/studio/profile/memory-profiler

Android 如何做一次内存泄漏大排查

内存计数中的类别如下:

  • Java:从 Java 或 Kotlin 代码分配的对象的内存。
  • Native:从 C 或 C++ 代码分配的对象的内存。

    即使您的应用中不使用 C++,您也可能会看到此处使用了一些原生内存,因为即使您编写的代码采用 Java 或 Kotlin 语言,Android 框架仍使用原生内存代表您处理各种任务,如处理图像资源和其他图形。

  • Graphics:图形缓冲区队列为向屏幕显示像素(包括

    GL

    表面、

    GL

    纹理等等)所使用的内存。(请注意,这是与 CPU 共享的内存,不是

    GPU

    专用内存。)
  • Stack:您的应用中的原生堆栈和 Java 堆栈使用的内存。这通常与您的应用运行多少线程有关。
  • Code:您的应用用于处理代码和资源(如

    dex

    字节码、经过优化或编译的

    dex

    代码、

    .so

    库和字体)的内存。
  • Others:您的应用使用的系统不确定如何分类的内存。
  • Allocated:您的应用分配的

    Java/Kotlin

    对象数。此数字没有计入

    C

    C++

    中分配的对象。

    如果连接到搭载

    Android 7.1

    及更低版本的设备,只有在内存性能分析器连接到您运行的应用时,才开始此分配计数。因此,您开始分析之前分配的任何对象都不会被计入。但是,

    Android 8.0

    及更高版本附带一个设备内置性能剖析工具,该工具可跟踪所有分配,因此,在

    Android 8.0

    及更高版本上,此数字始终表示您的应用中待处理的 Java 对象总数。
Android 如何做一次内存泄漏大排查

Heap Dump指标分析

只有看懂了每个指标,才能更好的分析内存,下面我们分析一下

heap Dump

指标

Android 如何做一次内存泄漏大排查

对象跟踪策略

为了在分析时提高应用性能,内存性能分析器在默认情况下会定期对内存分配进行采样。在运行 API 级别 26 或更高级别的设备上进行测试时,您可以使用

Allocation Tracking

下拉菜单更改此行为。可用选项如下:

  • Full:捕获内存中的所有对象分配。这是 Android Studio 3.2 及更低版本中的默认行为。如果您有一个分配了大量对象的应用,可能会在分析时观察到应用的运行速度明显减慢。
  • Sampled:定期对内存中的对象分配进行采样。这是默认选项,在分析时对应用性能的影响较小。在短时间内分配大量对象的应用仍可能会表现出明显的速度减慢。
  • Off/None:停止跟踪应用的内存分配。
    Android 如何做一次内存泄漏大排查

记录某一段时间的内存分配情况

Heap Dump

是一个很好用的工具,能够分析内存中所有的对象,但是也是有弊端的,

Heap Dump

是全量分析,如果你想分析某一段时间内的内存增量分配情况,该怎么做呢?点击

Record

按钮.

Android 如何做一次内存泄漏大排查

下面用一个 gif 看看

Android 如何做一次内存泄漏大排查

Memory Analyzer(MAT)

Memory Analyzer

工具,简称:

MAT

MAT

是 Eclipse 下的一个软件,专门用来分析 Java内存堆。

官方下载地址:https://www.eclipse.org/mat/

安装完成后,图标如下

Android 如何做一次内存泄漏大排查

不得不说,这个工具长得很丑。

MAT

可以打开

.hprof

, 但是从 AndroidStudio 里面的导出的

.hprof

文件,

MAT

是不支持查看的,所以需要转化一下,Android SDK 自带了转化工具。

您可以使用

android_sdk/platform-tools/

目录中提供的

hprof-conv

工具执行此操作。运行包含两个参数(即原始 HPROF 文件和转换后 HPROF 文件的写入位置)的

hprof-conv

命令。例如:

hprof-conv heap-original.hprof heap-converted.hprof

经过转化过的

.hprof

文件,

MAT

就可以打开了。

Android 如何做一次内存泄漏大排查

祝福

快过年了,祝大家 2021 事事顺心,万事大吉。新年快乐鸭 !!