android中momery检测,Android性能优化第(二)篇---Memory Monitor检测内存泄露

版权声明:本文为LooperJing原创文章,转载请注明出处!

ef9081050f5c

多练习多写代码.jpg

上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出来。性能优化的重要性不需要在强调,但是要强调一下,我并不是一个老司机,嘿嘿!没用过这个工具的,请睁大眼睛。如果你用过,那么就不用在看这篇博客了。

先看一段会发生内存泄露的代码

public class UserManger {

private static UserManger instance;

private Context context;

private UserManger(Context context) {

this.context = context;

}

public static UserManger getInstance(Context context) {

if (instance == null) {

instance = new UserManger(context);

}

return instance;

}

}

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

UserManger userManger = UserManger.getInstance(this);

}

}

代码很简单,就是一个单利模式泄露的场景,我们现在的关心的不是代码本身,而是如何将代码里面的内存泄露给找出来。但是对于上面的代码发生内存泄露的原因还是有必要提一下。

上篇博客说了,内存泄漏产生的原因是:当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而就导致,对象不能被回收。这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。

在上面的代码中,发生泄露的不是UserManger,而是MainActivity,UserManger中有一个静态成员instance,其生命周期和应用程序的生命周期一致,当退出应用时,才能被销毁,但是当GC准备回收MainActivity时,结果呢MainActivity的对象(this)在被UserManger所引用,UserManger本身又不能被干掉,所以就发生了内存泄露。

ef9081050f5c

monitors.png

Memory Monitor是Android Monitors中的一种,Monitors主要包括四种,Memory Monitor ,CPU Monitor ,NetWork Monitor, GPU Monitor ,今天介绍的是Memory Monitor ,其他的Monitor,在后面也准备讲。

Memory Monitor界面

ef9081050f5c

Memory Monitor.png

图中水平方向是时间轴,竖直方向是内存的分配情况

图中深蓝色的区域,表示当前正在使用中的内存总量,浅蓝色或者浅灰色区域,表示空闲内存或者叫作未分配内存。

左上角工具栏三个圆圈按钮依次代表

GC按钮 ,可以手动GC,回收程序垃圾

内存快照(Dump Java Heap) ,点击可以生成一个文件(包名+日期+“.hprof”),可以记录摸一个时间点内,程序内存的情况

Allocation Traking ,点击一次开始, 再次点击结束,也可以可以生成一个文件。

回到我们的程序,多点击几次GC,看一下这个应用的内存使用情况。

ef9081050f5c

内存使用情况.jpg

可以看到现在已经分配的内存有19.68M,我把手机旋转一下,在看。

ef9081050f5c

旋转后内存使用情况.png

可以看到现在的内存使用量是21.09M,还是一样的界面,却多了1.41M!!!这很关键。

接下来,我们找一下,哪里发生了泄露。点击Dump Java Heap,生成快照文件tool.test.memory.memoryleak_2016.11.13_21.38.hprof,Android Studio 自动弹出HPROF Viewer来分析它。

ef9081050f5c

快照文件分析.png

现在介绍一下HPROF Viewer的用法

HPROF Viewer查看方式

左上角两个红框,是可选列表, 分别是用来选择Heap区域, 和Class View的展示方式的.

Heap类型分为:

App Heap -- 当前App使用的Heap

Image Heap -- 磁盘上当前App的内存映射拷贝

Zygote Heap -- Zygote进程Heap(每个App进程都是从Zygote孵化出来的, 这部分基本是framework中的通用的类的Heap)

Class List View -- 类列表方式

Package Tree View -- 根据包结构的树状显示

我通常点击App heap下面的Classs Name把Heap中所有类按照字母顺序排序,然后按照字母顺序查找。

HPROF Viewer主要分ABC三大板块

板块A:这个应用中所有类的名字

版块B:左边类的所有实例

板块C:在选择B中的实例后,这个实例的引用树

A板块左上角列名解释

列名

解释

Class Name

类名,Heap中的所有Class

Total Count

内存中该类这个对象总共的数量,有的在栈中,有的在堆中

Heap Count

堆内存中这个类 对象的个数

Sizeof

每个该实例占用的内存大小

Shallow Size

所有该类的实例占用的内存大小

Retained Size

所有该类对象被释放掉,会释放多少内存

B板块右上角上角列名解释

列名

解释

Instance

该类的实例

Depth

深度, 从任一GC Root点到该实例的最短跳数

Dominating Size

该实例可支配的内存大小

B板块右上角有个"的按钮, 点击会进入HPROF Analyzer的hprof的分析界面:

ef9081050f5c

Analyzer Tasks.png

"

在这个界面中可以直接把内存泄露可能的类找出来。

下面分析一下MainActivity的泄露情况

ef9081050f5c

MainActivity发生内存泄露.png

一个Activity应该只有一个实例,但是从A区域来看 total count的值为2,heap count的值也为2,说明有一个是多余的。

在B区域中可以看见两个MainActivity的实例,点击一个看他的引用树情况

在C区域中可以看到MainActivity的实例Context被UserManger的 instance引用了,引用深度为1.

在Analyzer Tasks 区域中,直接告诉你Leaked Activities,MainActivity包含其中

多方面的证据表明MainActivity发生了内存泄露

解决方案

public class UserManger {

private static UserManger instance;

private Context context;

private UserManger(Context context) {

this.context = context;

}

public static UserManger getInstance(Context context) {

if (instance == null) {

if(context!=null){

instance = new UserManger(context.getApplicationContext());

}

}

return instance;

}

}

不要用Activity的Context,因为Activity随时可能被回收,我们用Application的Context,Application的Context的生命周期是整个应用,不回收也没有关系。

Memory Monitor获得内存的动态视图,Heap Viewer显示堆内存中存储了什么,可惜Heap Viewer不能显示你的数据具体分配在代码的何处,如果还不过瘾,想知道具体是哪些代码使用了内存,还有一个功能是Allocation Tracker,用来内存分配追踪。在内存图中点击途中标红的部分,启动追踪,再次点击就是停止追踪,随后自动生成一个alloc结尾的文件,这个文件就记录了这次追踪到的所有数据,然后会在右上角打开一个数据面板

Allocation Tracker启动追踪

ef9081050f5c

Allocation Tracker启动追踪.png,

Allocation Tracker查看方式

ef9081050f5c

Allocation Tracker查看方式

有两种查看方式,默认是Group by Method方式

Group by Method:用方法来分类我们的内存分配

Group by Allocator:用内存分配器来分类我们的内存分配

从上图可以看出,首先以线程对象分类,Size是内存大小,Count是分配了多少次内存,点击一下线程就会查看每个线程里所有分配内存的方法

Group by Method方式

ef9081050f5c

每个线程里所有分配内存的方法.png

OK,-Memory Monitor

** Group by Allocator方式**

ef9081050f5c

EY%HY_B74%BUE22C6$G~CTP.png

右键可以直接跳到源码

- 扇形统计图

ef9081050f5c

AQFHT$@7TYP0S_1`DU@%S%6.png

点击统计图按钮,会生成上图,扇形统计图是以圆心为起点,最外层是其内存实际分配的对象,每一个同心圆可能被分割成多个部分,代表了其不同的子孙,每一个同心圆代表他的一个后代,每个分割的部分代表了某一带人有多人,你双击某个同心圆中某个分割的部分,会变成以你点击的那一代为圆心再向外展开。

除了扇形图,还有柱状图可选择,可以自己操作,OK,Memory Monitor到此结束,下一篇性能优化部分博客仍然是检测内存泄露,明天上班,晚安!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值