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

上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过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本身又不能被干掉,所以就发生了内存泄露。

monitors.png

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

  • Memory Monitor界面

Memory Monitor.png

  • 图中水平方向是时间轴,竖直方向是内存的分配情况
  • 图中深蓝色的区域,表示当前正在使用中的内存总量,浅蓝色或者浅灰色区域,表示空闲内存或者叫作未分配内存。
  • 左上角工具栏三个圆圈按钮依次代表
    GC按钮 ,可以手动GC,回收程序垃圾
    内存快照(Dump Java Heap) ,点击可以生成一个文件(包名+日期+“.hprof”),可以记录摸一个时间点内,程序内存的情况
    Allocation Traking ,点击一次开始, 再次点击结束,也可以可以生成一个文件。

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

内存使用情况.jpg

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

旋转后内存使用情况.png

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

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

快照文件分析.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的分析界面:

Analyzer Tasks.png

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

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

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启动追踪

Allocation Tracker启动追踪.png

Allocation Tracker查看方式

Allocation Tracker查看方式

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

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

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

  • Group by Method方式

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

    OK,-Memory Monitor

  • Group by Allocator方式

    EY%HY_B74%BUE22C6$G~CTP.png

    右键可以直接跳到源码

- 扇形统计图

[email protected]_1`[email protected]%S%6.png

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

Memory Monitor可以发现的问题

Memory Monitor工具为监控工具,是一种发现型或者说监控性质的工具,比如医生的四大技能[望闻问切],[望]是第一步。这里的Memory Monitor就是一种[望]的工具,目前我主要用它来看下面几个内存问题:

1.发现内存抖动的场景

2.发现大内存对象分配的场景

3.发现内存不断增长的场景

4.确定卡顿问题是否因为执行了GC操作

案例分析

上面的第一段标记显示内存突然增加了7M,我们也能看的很清楚,所以这个点我们要去定位了一下问题在哪里,是Bitmap还是什么原因造成的,第二段标记是内存抖动,很明显在很短的时间了发生了多次的内存分配和释放。而且在发生内存抖动的时候,也能感觉到App的卡顿,可以看出来是由于执行了GC操作造成的。

内存的不断增加通过Memory monitor很容易看出来,蓝色的曲线是一路高歌猛进的,一看便知。

时间: 2024-10-03 23:15:44

Android性能优化第(二)篇---Memory Monitor检测内存泄露的相关文章

Android性能优化:手把手带你全面了解 内存泄露 & 解决方案

. 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM 内存溢出 简介: 示意图3. 发生内存泄露的本质原因 具体描述示意图 特别注意 从机制上的角度来说,由于 Java存在垃圾回收机制(GC),理应不存在内存泄露:出现内存泄露的原因仅仅是外部人为原因 = 无意识地持有对象引用,使得 持有引用者的生命周期 > 被引用者的生命周期4. 储备知识:Androi

[Android 性能优化系列]布局篇之减少你的界面层级

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/improving-layouts/optimizing-layout.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 性能优化之布局篇: [Android 性能优化系列]布

[Android 性能优化系列]布局篇之通过<include>复用布局

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/improving-layouts/reusing-layouts.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 性能优化布局篇: [Android 性能优化系列]布局篇之

[Android 性能优化系列]布局篇之动态加载布局

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/improving-layouts/loading-ondemand.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 性能优化布局篇: [Android 性能优化系列]布局篇

Android性能优化【终极篇】

1.http用gzip压缩,设置连接超时时间和响应超时时间 http请求按照业务需求,分为是否可以缓存和不可缓存,那么在无网络的环境中,仍然通过缓存的httpresponse浏览部分数据,实现离线阅读. 2.listview 性能优化 1).复用convertView 在getItemView中,判断convertView是否为空,如果不为空,可复用.如果couvertview中的view需要添加listerner,代码一定要在if(convertView==null){}之外. 2).异步加载

android性能优化实战理论篇

本文地址:http://blog.csdn.net/iamws/article/details/51636175 第二篇:理论 通过之前前篇介绍的工具,我们知道了应该怎么样去获取要分析的数据,但是也仅仅局限在于怎么样获取数据,而没有深入数据分析,这一篇主要讲解的是UI刷新这块部分android理论知识,有了这些知识后,对于上面的数据该怎么分析,你就胸有成竹了. ps:本文只是个人理解后的总结,并不会深入源码层次分析,如有错误,还请麻烦各位帮忙指正~ 这篇文章要解决的理论问题如下: 1.什么是内存

Android性能优化之被忽视的Memory Leaks

起因 写博客就像讲故事,得有起因,经过,结果,人物,地点和时间.今天就容我给大家讲一个故事.人物呢,肯定是我了.故事则发生在最近的这两天,地点在coder君上班的公司.那天无意中我发现了一个奇怪的现象,随着我点开我们App的页面,Memory Monitor中显示占用的内存越来越多(前面的页面已经finish掉了).咦?什么鬼? 经过 有了问题就解决嘛,俗话说的好,有bug要上,没有bug写个bug也要上.那到底是是什么问题会引起这个现象呢? Android中内存相关的问题无非就是这么几点: M

Android性能优化之渲染篇

下面是渲染篇章的学习笔记,部分内容和前面的性能优化典范有重合,欢迎大家一起学习交流! 1)Why Rendering Performance Matters 现在有不少App为了达到很华丽的视觉效果,会需要在界面上层叠很多的视图组件,但是这会很容易引起性能问题.如何平衡Design与Performance就很需要智慧了. 2)Defining 'Jank' 大多数手机的屏幕刷新频率是60hz,如果在1000/60=16.67ms内没有办法把这一帧的任务执行完毕,就会发生丢帧的现象.丢帧越多,用户

android性能优化实战前篇

本文地址:http://blog.csdn.net/iamws/article/details/51629160 前言: 最近因为某项目cpu,内存的使用率实在让人不敢恭维:手机发烫,电量下降已经让用户无法忍受:频繁快速迭代发版导致各种性能问题突出:由于之前产品不考虑低端手机情况,低端手机直接跑到崩溃,直接体现的是DAU的急速下降:最终在竞品的对比数据下,开始重视这块事情了,也给了我们半个月时间去优化性能. 目标: 性能达到并且低于竞品的消耗值,并且能让低配用户也能玩起来. 这就是这篇文章的来源