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
  • 图中水平方向是时间轴,竖直方向是内存的分配情况
  • 图中深蓝色的区域,表示当前正在使用中的内存总量,浅蓝色或者浅灰色区域,表示空闲内存或者叫作未分配内存。
  • 内存分析的工具栏,从上向下一共4个按钮,依次是:
    1. Enable 终止检测的开关,没什么实质性的作用

      InitGC 就是手动调用GC,我们在抓内存前,一定要手动点击 Initiate GC按钮手动触发GC,这样抓到的内存使用情况就是不包括Unreachable对象的(Unreachable指的是可以被垃圾回收器回收的对象,但是由于没有GC发生,所以没有释放,这时抓的内存使用中的Unreachable就是这些对象)

      DumpHeap (Dump Java Heap)点击可以生成一个文件(包名+日期+“.hprof”),可以记录摸一个时间点内,程序内存的情况。获取hprof文件(hprof文件是我们使用MAT工具分析内存时使用的文件),但这里直接产生的文件MAT还不能直接使用,需用转换成标准的hprof文件。可以使用AndroidStudio转换或者用hprof-conv命令转化,网上可以查到

      AllocTrack 开始分配追踪,第一次点击可以指定追踪内存的开始位置,第二次点击可以结束追踪的位置。这样我们截取了一段要分析的内存,等待几秒钟AndroidStudio会给我们打开一个Allocation视图(感觉和MAT工具差不多,不过MAT工具更加强大,我们也可以获取hprof文件,使用MAT来分析)

回到我们的程序,多点击几次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

点击Analyzer Tasks右边的绿色运行箭头,Android Studio会自动的根据此hprof文件分析有哪些类是有内存泄漏的,如下图所示: 


下面分析一下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


    右键可以直接跳到源码

- 扇形统计图


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

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

 

Memory Monitor可以发现的问题

Memory Monitor工具为监控工具,是一种发现型或者说监控性质的工具,比如医生的四大技能[望闻问切],[望]是第一步。这里的Memory Monitor就是一种[望]的工具,目前我主要用它来看下面几个内存问题: 
1.发现内存抖动的场景 
2.发现大内存对象分配的场景 
3.发现内存不断增长的场景 
4.确定卡顿问题是否因为执行了GC操作

案例分析

这里写图片描述

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

 


    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6628311.html,如需转载请自行联系原作者



相关文章
|
2月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
58 2
|
2月前
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
108 1
|
29天前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
1月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
33 5
|
1月前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
51 3
|
2月前
|
存储 缓存 网络协议
5个Android性能优化相关的深度面试题
本文涵盖五个Android面试题及其解答,包括优化应用启动速度、内存泄漏的检测与解决、UI渲染性能优化、减少内存抖动和内存溢出、优化网络请求性能。每个问题都提供了详细的解答和示例代码。
28 2
|
2月前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
51 2
|
2月前
|
监控 测试技术 Android开发
掌握安卓性能优化的关键策略
【10月更文挑战第7天】 在移动应用开发领域,性能优化是一项至关重要的任务。本文将探讨安卓应用性能优化的重要性、关键策略以及实际操作建议,帮助开发者提升应用的用户体验和竞争力。通过深入浅出的方式,我们将从背景介绍到具体实践,全面解析安卓性能优化的各个维度。
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
404 0
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
81 1