Android内存泄漏的检测流程、捕捉以及分析

https://blog.csdn.net/qq_20280683/article/details/77964208

简述:

一个APP的性能,重度关乎着用户体验,而关于性能检测的一个重要方面,就是内存泄漏,通常内存泄漏的隐藏性质比较强,不同于异常导致的程序Crash,在异常导致的Crash中,我们能够及时的发现程序问题的存在,并通过log日志定位到问题所在的具体位置,然后及时进行解决,而内存泄漏则不同,在APP中存在内存泄漏的情况下,用户在低频率短时间的使用中,并不能察觉到有什么异样,反之,随着使用频率的提高和使用时长的增加,内存泄漏就会一直慢慢积累,消耗内存,从而会导致手机卡顿,直至APP崩溃,所以防止APP内存泄漏的出现,是至关重要的。

理论阐述内存泄漏导致的原因:

在android开发中,jvm具有自动回收的机制,会不定时不定期的去清理无用的被占用的内存,而在理论上不需要再被使用的内存,在实际中却还持有对这一块内存的引用,导致GC时,不会被回收释放掉,这部分内存就会随着程序的运行不断堆积,从而导致应用分配的内存不够使用导致卡顿、ANR异常等情况。

导语

关于内存泄漏的检测,我们分为了以下几个阶段:
1. 开发编码过程中,在开发过程中就不断对代码进行内存泄漏的检测
2. 项目或者模块开发完成后,对应用进行整体的内存泄漏检测
3. 在项目上线后,远程端检测项目是否存在内存泄漏的情况

一:开发编码过程中,检测内存泄漏

编码过程中可能导致的内存泄漏案例分析:

关于在编码中可能导致的泄漏案例以及编码注意事项,本文不做过多赘述,在给到的链接中,作者对泄漏案例描述的都比较详细,也比较全面,想了解的小伙伴可以【点击这里—>内存泄漏案例】

1.检测工具:LeakCanary

首先最容日上手并且效果还不错,那就要属LeakCanary,效果也直观,具体的使用配置也很简单。

在项目的build.gradle中加入以下引用:
// 内存存泄漏检测
debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.5‘
releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5‘
testCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5‘
1
2
3
4
在application中初始化LeakCanary,到此处配置完成
/** Explain : 初始化内存泄漏检测
* @author LiXaing
private void initLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
1
2
3
4
5
6
7
8
发生内存泄漏提示

当前就从网络上获取到了一张关于,在发生内存泄漏的时候,会在通知栏出现一个提示图标,当点击进去之后,就是现在展示的这张图片,会直观的展示内存泄漏的位置。

注意:==通过LeakCanary的使用,它可以为我们快速找到内存泄漏的位置,但并不能够提供我们内存泄漏的原因,有的时候,内存泄漏的位置是由于其他原因导致的,本人曾经就碰到过一次,由于Fragment未能被回收,从而导致了EventBus未能解绑(在onDestory中有解绑EventBus),导致的EventBus也存在内存泄漏,而导致的原因并非是没有解绑;所以内存泄漏的位置,并不一定就是导致泄漏的根本原因,所以后面及有可能还需要其他的工具进行辅助。==

2.检测工具:StrictMode

StrictMode在Android 2.3(API 9)的时候就已经引入了,虽然到当前这个工具年代比较久远了,但属实还是非常好用的, 在开发阶段使用这个工具,能够很好的帮助发现开发中的一系列不规范的编码,例如主线程访问网络,主线程读写磁盘,等等耗时操作,另外的一大特性就是可以帮助开发时,发现程序存在内存泄漏的情况。

StrictMode的功能主要分为两大块:

一块是关于Thread,线程规范的监测,另一块是关于VM,内存的监测。在StrictMode下分别是ThreadPolicy和VMPloicy。

ThreadPolicy:用于监测线程部分,监测 主线程中是否访问网络、主线程中是否读写磁盘等。

<!--代码示例:在application中进行配置即可-->
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()//监测所有内容
.penaltyLog()//违规对log日志
.penaltyDeath()//违规Crash
.build());
1
2
3
4
5
6
相关API:

API 描述
detectAll 监测所以违规内容
permitAll 禁用所以监测内容
permit**…如:permitNetwork 关闭检测网络访问违规
detectNetwork 监测主线程中是否存在有访问网络
detectDiskReads()、detectDiskWrites() 监测是否在主线程中读写磁盘
penaltyDeath 一旦触发任何违规操作就直接Crash掉程序
penaltyDeathOnNetwork 一旦触发网络访问违规操作就Crash掉程序
penaltyDialog 一旦触发违规操作,就弹出违规信息对话框
当然,以上列出的是常用的API,也还有其他的一些API没有列出来,在开发中以上的API基本够用了,要是还有其他需求的小伙伴,就自行去查找一下吧。

VMPolicy:用于监测内存,可以监测Activity的内存泄漏,Fragment的内存泄漏(虽然内部没有指定Fragment可用的API但内部关于类对象的检测机制在此处就有着异曲同工之妙的作用),SQL内存泄漏,是否正常关闭读写流以及可以指定某个类的最大对象数目。

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()//监测所以内容
.penaltyLog()//违规对log日志
.penaltyDeath()//违规Crash
.build());
1
2
3
4
5
API 描述
detectAll 监测所以违规内容
detectActivityLeaks 监测Activity内存泄漏的情况
detectLeakedClosableObjects() 和 detectLeakedSqlLiteObjects() 当使用的资源没有被正确关闭时会触发
detectLeakedRegistrationObjects 监测BroadcastReceiver 或者 ServiceConnection 注册类对象是否被正确释放
setClassInstanceLimit 设定某个类在内存中实例的上限
penaltyDialog 一旦触发违规操作,就弹出违规信息对话框
当然,以上列出的是常用的API,也还有其他的一些API没有列出来,在开发中以上的API基本够用了,要是还有其他需求的小伙伴,就自行去查找一下吧。

二:项目或者模块开发完成后,检测内存泄漏

在项目或者模块开发完成后,所需要做的就是各种测试,在交给测试人员之前,为了展现我们自身编码的高逼格和高质量,通常我们程序猿自己先会进行一部分测试,其中很重要的一部分测试就是本章内容讲的,内存泄漏的测试。

首先,我们可以使用android Studio中AndroidMonitor自带的一个工具—>memory,这个工具也可以说是非常的好用,先简单的介绍一下,memory虽然不可以分析出哪部分存在泄漏等情况,但可以很直观的看到内存的占用情况,看到内存的动态变化。

1. 我们打开AndroidMonitor,点击打开Memory,打开的界面,可能是这样的什么都没有。

这时我们需要需要注意两个点:
2. 可能是没有选择相关的进程,点击下三角,选择我们需要查看相关的进行。

如果还是什么都没有,我们点击 “Tools->Android->Enable ADBIntergration(勾选)”

操作好以上步骤之后,我们就可以看到上面这张图片的状态,可以很直观的看到,应用在内存中占用的情况。

使用Memory,并不能够分析出具体的是哪一部分存在内存泄漏,主要是用来查看内存的占用的动态情况

关于左侧上方四个按钮功能和用法的描述:

功能 描述
Memory的开关
(Initial GC) 用于手动GC,通常在抓取HPROF文件前,手动GC回收掉那些无用对象
(Dump Java Heap) 用于生成HPROF文件,HPROF文件中的的数据是点击这个生成按钮这一瞬java VM执行时共享Heap中的数据,共享Heap中的数据主要包含了类的实例和数组对象,通常用于内存泄漏分析,在点击之后就能够看到Memory recorder在转动,在稍许延迟之后就会生成一个HPROF文件。
(Starg Allocation Tracking) 用于动态追踪内存情况,可以记录下一段时间区间内各个线程中各个方法在内存中的分配情况,使用方式:点击Starg Allocation Tracking按钮,开始分配追踪,可以看到Memroy recorder在转动,在合适的时间再点击一下就完成了对一时间区间内内存动态的记录,在稍许延迟之后就会生成一个.alloc文件
Dump Java Heap生成的.HPROF文件

在我们点击 Dump Java Heap之后,就会生成一个.alloc文件,这个文件是分析内存泄漏非常重要的一部分。

当打开HPROF文件的时候是这样的,什么都没有,为了方便查找,我们点击”Class List View”选择–>”Package Tree View”当前展示就是变为包的视图树。

那么到了这一部分,我们应该查看哪儿是存在泄漏的呢?分为两种情况:

1. 其实如果到了这一步,基本上可以断定是存在内存泄漏了的,通过之前的LeakCanary、StrictMode已经可以判断出内存泄漏了,而这部分是为了通过LeakCanary、StrictMode给出的信息更确切的确定内存泄漏的部分在内存中运作的情况和更详细的数据。

2. ==首先需要明确的一点是LeakCanary、StrictMode并不能检查出所有的内存泄漏==,所以在我们对应用程序反复点击调试之后,需要我们人工审查这些类的实例,通常绝大多数类的实例例如Activity、Fragment等等每个类的实例是只有一个的,当然也是要看具体的程序实现来看待,在ViewPage中等情况就可以出现一个类有多个实例的情况,这也因实际情况来看待,如果超出了实际情况的实例对象数那么就很有可能是存在了内存泄漏。在我们人工审查这些类的实例通常都会先检查Activity、Fragment、自定义View等等的实例情况,因为伴随这些一起泄漏的都是高概率。

给出的上图,是Fragmnet反复打开关闭产生实例的泄漏,实际情况中只应该存在一个,而此处有14个,有人会问,那你怎么就知道就要查找这一个Fragmnet实例呢?在此,需要说明的一点是,不管是通过上面所说的第一种情况还是第二种,都可以排查到这类泄漏的;要问:那要我没有排查到呢?那只有说:眼瞎,就先去医院治病~

在给出的上图中,存在多个Fragment内存泄漏,我们就关注图中标注好的“1”部分,我们可以通过Total Count(总实例数)和Heap Count(堆内存中实例数)可以看到有14个,点击这个类,然后在旁边的Instance框中我们可以看到这14个具体的实例,也就是图中标注的“2”部分,我们点击第一个;然后在下方的Reference Tree中我们可以看到当前这个实例对象持有的具体的对象,那么在这一部分我们怎么排查呢?我们主要需要关注的是 Dominating Size(当前指向的这个一条,在内存中占有的大小)值最大的前面几条,为什么呢?因为泄漏导致内存无法被释放值越大,存在泄漏的可能性越大。 所以看上图中的“3”部分,我们针对这一条不断的向下展开引用,在“4”部分,我们可以很清晰的看到是由EventBus导致的内存泄漏,而在我给出的这个示例里,也就是由于Fragment在销毁时未EventBus导致的。

另外值得一说的标注的“5”,在Analyzer中有一个功能就是 Detect Leaked Activities,点击绿色三角按钮运行后,可以帮我们分析出当前可能存在泄漏的Activity对象。

到目前为止,我们通过HPROF文件在Studio中的使用可以完成内存泄漏的分析,不过还有更为强大的一款工具,更方便我们使用就是下要说的MAT

MAT(Memory Analyzer Tool)工具的使用

【还没有这个工具的小伙伴可以戳这里–>Eclipse Memory Analyzer Open Source Project】

首先我们需要打开在MAT中打开.hprof文件,在打开MAT工具后,点击右上角的 “File”-> “Open Heap Dump”,然后选择需要打开的.hprof文件,在打开的过程中可能会遇到这样的问题:

关于这个问题,有两种方式解决,比较建议的是第一种,第一种更方便,更容易操作:

一:

在Android Studio工具的右侧,点击上图红框标注的“1” CapTures;

选择红框标注的“2”Heap SanPshot选择需要导出的.hprof;

选择好需要导出的.hprof文件后,单机右键选择“Export to standard”(导出标准的.hprof文件);
4.然后选择导出保存到的位置,就可以了。

二:
Windows操作系统下,打开CMD黑窗口(win键+R键,输入cmd)

按照上图的操作写入输入输入路径,然后回车,就可以完成转换了。

接下来就是关于在MAT工具中的操作,MAT的功能很多,但就不一一介绍了,就简单说明关于在检测内存泄漏用到的部分。

选择上图红框的“Histogram”,然后我们可以看到下图界面

在上图中,我们看到的是一系列的数组对象和类的实例等数据,我们可以通过上图红框中的部分,输入需要查找的路径或者类的类名等进行过滤

在这一部分中,所需要观察的点,就是Objects列,是存在的实例对象数,我们选择好了需要排查的实例对象点击右键,选择“merge shortest Path to GC Roots” -> “exclude all phantom/weak/soft etc. references”,而为什么要进行这一步过滤呢,因为导致内存泄漏的只会是硬引用,所以我们可以把虚引用,弱引用,软引用全部进行过滤掉,可以减少误判。

在进行过滤之后,我们可以看到唯一持有的硬引用只有EventBus,那么这时,我们回到项目中去具体观察EventBus部分的代码就好了;而这,只是在实际情况中的一种,另外在过滤掉那三种引用之后,还存在有多种引用的情况,这个时候,需要观察的点就是“Objcets”(对象数量)和Showll Heap(在堆内存中占据的内存大小)数量越多和占据内存越高的,存在泄露的可能性越大,因为反复创建却不能够被回收,数量和占据的内存越高。

原文地址:https://www.cnblogs.com/jukan/p/10361338.html

时间: 2024-08-08 01:27:43

Android内存泄漏的检测流程、捕捉以及分析的相关文章

android 内存泄漏检测工具 LeakCanary 泄漏金丝雀

韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha [email protected] 内存泄漏检测工具 android 内存泄漏检测工具 ======== 内存泄漏 就是  无用的对象没有被回收,占用着内存,使得可用内存变小了. 如何检测内存泄漏, 可以使用 LeakCanary来检测内存泄漏. leak  是 泄漏的意思.. Canary 是 金丝雀 的意思. 在运行 应用的时候, 泄漏金丝雀 如果检测到内存泄漏 会显示一个通知. ======== LeakCanary捕获

android内存泄漏检测StrictMode和MAT工具使用

StrictMode说明 Android 2.3提供一个称为严苛模式(StrictMode)的调试特性,Google称该特性已经使数百个Android上的Google应用程序受益.那它都做什么呢?它将报告与线程及虚拟机相关的策略违例.一旦检测到策略违例(policy violation),你将获得警告,其包含了一个栈trace显示你的应用在何处发生违例.你可以强制用警告代替崩溃(crash),也可以仅将警告计入日志,让你的应用继续执行.策略的细节尚难确定,可以期待随Android的成熟Googl

Android —— 内存泄漏检查

今天地铁上看到一篇不错的将内存泄漏简单检查的文章,觉得还不错哟,内存泄漏确实是每个程序员头疼的事情,这里就多研究一下咯^^ 一. 常见的垃圾回收算法 参看文章 引用计数法 引用计数法基本上最简单的垃圾回收策略,它的核心思想是: 当有指针指向某实例时,计数加一, 当删除一个指针时,计数减一,当计数为0时,说明该实例没有引用可以被垃圾回收器回收. 这种回收策略的缺点是显而易见的: 1.维护引用计数是有开销的 2.计数的保存会消耗额外的空间 3.无法处理循环引用 标记清除 标记清除,顾名思义分为2步:

Android内存泄漏查找和解决

Android内存泄漏查找和解决 目录: 内存泄漏的概念 一个内存泄漏的例子 Java中"失效"的private修饰符 回头看内存泄漏例子泄漏的重点 强引用与弱引用 解决内部类的内存泄漏 Context造成的泄漏 使用LeakCanary工具查找内存泄漏 总结 一.内存泄漏概念 1.什么是内存泄漏? 用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元.直到程序结束.即所谓的内存泄漏. 其实说白了就是该内存空间使用完毕之后未回收 2.内存泄漏会导致的问题 内

Android 性能篇 -- 带你领略Android内存泄漏的前世今生

基础了解 什么是内存泄漏? 内存泄漏是当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗.内存泄漏并不是指物理上的内存消失,这里的内存泄漏是指由程序分配的内存但是由于程序逻辑错误而导致程序失去了对该内存的控制,使得内存浪费. Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是 静态分配 . 栈式分配 和 堆式分配 ,对应的三种存储策略使用的内存空间主要分别是 静态存储区(也称方法区) . 栈区 和 堆区 . ?? 静态存储区(方法区):主要存放 静态数据 . 全局

android 内存泄漏分析技巧

java虚拟机运行一般都有一个内存界限,超过这个界限,就会报outofmemory.这个时候一般都是存在内存泄漏.解决内存泄漏问题,窃以为分为两个步骤:分析应用程序是否真的有内存泄漏,找到内存泄漏的地方.这两个步骤都不是一般意义上的调试,直接打log,断点调试都不是太给力.动脑筋想一想,内存问题应该在很多地方上都会出现,这么常见的问题应该是有工具的.android现在更可以说是一个生态系统,当然也有很多开发辅助工具.在前面的两个步骤中都有很强大的武器,熟练的掌握这些利器,分析问题就会事半功倍.

内存泄漏及其检测工具

一.什么是内存泄露? 在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 通常我们所说的内存泄漏是指堆内存的泄漏.堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存.应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或del

android内存泄漏系列- 分析hprof文件

转载请注明出处 http://www.cnblogs.com/weiwangnuanyang/p/5703702.html ,谢谢. 如果只是想确定一下某一个场景是否有内存泄漏,AndroidStadio的控制台就有一个好工具,反复操作观察曲线是否上扬,如果曲线上扬则说明内存泄漏 但是,上面的工具不够强大,不能看出内存中驻留的具体的类和类的引用关系. 下面就来重点介绍一下,解决android内存泄漏必备利器-Memory Analysis; 具体安装方式请移步度娘. 我们这里重点介绍如何利用Me

Android 内存泄漏总结

Java中的内存泄漏 java内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收.在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连:其次,这些对象是无用的,即程序以后不会再使用这些对象.如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存. 在C++中,内存泄漏的范围更大一些.有些对象被