Android 如何做一次内存泄漏大排查 您所在的位置:网站首页 jni内存泄露怎么办 Android 如何做一次内存泄漏大排查

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

2023-09-05 04:26| 来源: 网络整理| 查看: 265

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/112335970 本文出自【赵彦军的博客】

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

前言

眼瞅着还有一个月就过年,项目也没有那么忙了,技术老大要求做一做性能优化方面的工作。 而我的任务就是把项目中的内存泄漏撸一遍,然后安排对应的人处理。

说到内存泄漏,我也算是老手了,其实在 2016 年我就写个内存泄漏方面的文章:

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

把内存泄漏的地方找出来

说干就干,首要的任务就是把内存泄漏的代码揪出来,我选择 leakcanary github 地址:https://github.com/square/leakcanary/

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

我只啰嗦一点:

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

集成完成后,把项目跑起来,过一会就会报出来很多内存泄漏的日志。 在 Android Studio logcat 过滤 LeakCanary 就会看到如下: 在这里插入图片描述 同时在手机桌面也会有一个 小鸟图标,点击会看到可视化页面 在这里插入图片描述 这里就可以看到内存泄漏的链条,分析的方式是 从下往上 的顺序,比如针对本图:

首先 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 文件地址:

在这里插入图片描述 我们拿到 hprof 文件链接后,就可以通过 adb pull 命令导到电脑桌面

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

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

在这里插入图片描述 点击 Share Heap Dump file 可以通过分享出去。

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

如何打开 hprof 文件

方式一:Android studio Profiler 功能打开 在这里插入图片描述 这个工具显示了如下信息:

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

用HPROF分析工具,可以检测到泄漏的 Activity 在这里插入图片描述 通过这个可以看到 本次分析有 3 出泄漏的地方,点击 第一个 ReaderActivity 在这里插入图片描述 可以看到详细的泄漏实例,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 --> 点击 向下的箭头

在这里插入图片描述 由于生成的 hprof 文件比较大,所以解析出来比较慢,要耐心等待。

在这里插入图片描述

至此,我们就完成手动 Heap Dump 操作,并且生成 hprof 文件 。我们也可以点击保存按钮,把 hprof 文件保存到桌面,或者发给其他人。

在这里插入图片描述

如何代码触发 Heap Dump

代码其实很简单:

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

生成的文件在 Android/data/app包名/cache/ 目录下: 在这里插入图片描述

如何手动触发 GC

AndroidStudio --> Profiler --> 点击 + 号 --> 选择设备 --> 选择进程 --> 点击 MEMORY --> 点击 像垃圾桶 的图标

在这里插入图片描述 其实最快速的是点击 右键 在这里插入图片描述

Android Profiler指标

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

在这里插入图片描述 内存计数中的类别如下:

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 对象总数。

在这里插入图片描述

Heap Dump指标分析

只有看懂了每个指标,才能更好的分析内存,下面我们分析一下 heap Dump 指标 在这里插入图片描述

对象跟踪策略

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

Full:捕获内存中的所有对象分配。这是 Android Studio 3.2 及更低版本中的默认行为。如果您有一个分配了大量对象的应用,可能会在分析时观察到应用的运行速度明显减慢。Sampled:定期对内存中的对象分配进行采样。这是默认选项,在分析时对应用性能的影响较小。在短时间内分配大量对象的应用仍可能会表现出明显的速度减慢。Off/None:停止跟踪应用的内存分配。 在这里插入图片描述 记录某一段时间的内存分配情况

Heap Dump 是一个很好用的工具,能够分析内存中所有的对象,但是也是有弊端的,Heap Dump 是全量分析,如果你想分析某一段时间内的内存增量分配情况,该怎么做呢?点击 Record 按钮.

在这里插入图片描述 下面用一个 gif 看看

在这里插入图片描述

Memory Analyzer(MAT)

Memory Analyzer 工具,简称:MAT 。

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

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

安装完成后,图标如下 在这里插入图片描述 不得不说,这个工具长得很丑。

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 就可以打开了。

在这里插入图片描述

祝福

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



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有