Android 处理内存泄漏的方式

本篇博客为Android内存泄漏的简单分析,以使用android studio 中的android monitor 工具为主要分析工具,MAT为辅助分析工具(本文中未提及具体使用方法)。

我们从以下几个问题来依次分析,探究下到底应该如何去处理android中的内存泄漏问题。

1.什么是内存泄漏?

2.GC到底是如何工作的?

3.怎么去检测项目中是否有内存泄漏?

4.如何去处理内存泄漏。

1.什么是内存泄漏?

关于内存泄漏,我想每个人都对此都有一个简单的宏观概念,但是内存泄漏究竟是什么呢?我们需要用科学的方法对其进行阐述:

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连。其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

而在我的理解中:内存泄漏是指“内存不在GC的掌控范围之内了”。

在我们的项目中,如果需要被回收的对象没有被GC回收,就会造成内存泄漏,过多的内存泄漏会导致内存应用程序内存持续增大,导致OOM(Out Of Memory)。

2.GC到底是如何工作的?

GC是垃圾收集的意思(Garbage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃。
Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。 

在JAVA的GC机制中,GC Roots(Garbage Collector)作为垃圾回收器的对象,显得尤为重要。
GC会收集那些不是GC roots且没有被GC roots引用的对象。
一个对象可以属于多个root,GC root有几下种:

Class - 由系统类加载器(system class loader)加载的对象。

这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots。

Thread - 活着的线程

Stack Local - Java方法的local变量或参数:静态和非静态引用

JNI Local - JNI方法的local变量或参数

JNI Global - 全局Native方法中JNI引用的对象

Monitor Used - 用于同步的监控对象

Held by JVM - 用于JVM特殊目的由GC保留的对象。

但实际上Held by JVM是与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此就只有留给分析分员去确定哪些是属于”JVM持有”的了

而不被GC Root引用的对象被称为垃圾对象,则会自动被JAVA的GC机制回收。

3.怎么去检测项目中是否有内存泄漏?

(1)Leak Canary

相信大家都对LeakCanary有一定的了解,作为一款开源的内存泄漏检测工具,它能满足我们的大部分需求:

如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。

这里对其工作原理及使用方法不做赘述,想了解的同学可以点击这里

(2)使用Android Studio 中的Android Monitor工具

Step1:粗略的进行判断

我们可以先使用Monitor工具粗略的对项目进行检查,查看我们的项目是否存在内存泄漏。

步骤:
1、打开Andorid Monitors 中的Monitor
2、点击左边第三个小图标(System Information)
3、点击Memory Usage
这里写图片描述

4、选中之后会生成一个以包名和时间命名的txt的文件,如下图。

这里写图片描述

命令行模式下查看: adb shell dumssys meminfo 包名 -d

其中上方为Momery Info,我们可以大致的看出内存中各类型的数量及持有的内存信息(这里想了解的可以查阅相关的资料,我对此理解还不透彻)。

下方Object根节点下为各类型对象的数量及持有的内存信息。根据途中的情况,我们可以观察到在程序退出,进程未杀死时,存在一个Acticity的引用,5个AppContextde引用。

由此可以判断我们的项目中存在内存泄漏。

除此方法外我们也可以用过指定的动作(如Activity之间的跳转)发生前后(各调用一次GC),Android Monitor中Memory的变化来大致的判断前后内存是否发生变化,来确定我们的项目中是否有内存泄漏。

Step2:精准查找内存泄漏源头

运行我们的项目,手动测试(把可能存在内存泄漏的页面重复打开关闭),然后回到首页先点击Monitor下的Initiate GC按钮(多点几次,因为有时候不能及时的被调用),再点击Dump java Heap按钮(生成堆栈快照),会自动生成一个以包名和时间命名的hporf文件。
这里写图片描述

这里写图片描述

我们以ClassListView的形式查看该文件

这里写图片描述

到这里我们已经精确的知道了哪里存在了内存泄漏。
我们还可以通过MAT工具全面的分析内存泄漏报告(.hporf),对比多个堆栈快照之间的差异来得出内存泄漏的信息并修改,这一部分不赘述,有兴趣的同学自行百度Google下,蛮多资料的。

4.如何去处理内存泄漏。

既然已经找到了哪些代码存在内存泄漏,那修改起来就很简单了。只需要即时的将不需要的对象置为Null,或者调用对象的destory,unSuberXxx等方法就可以了。

当然,良好的代码习惯非常重要(静态变量的处理等等)。

这一点后续补充。

猜你喜欢

转载自blog.csdn.net/qq564045867/article/details/53812642