zoukankan      html  css  js  c++  java
  • Android内存优化(二)解析Memory Monitor、Allocation Tracker和Heap Dump

    前言

    要想做好内存优化工作,就要掌握两大部分的知识,一部分是知道并理解内存优化相关的原理,另一部分就是善于运用内存分析的工具。本篇就来介绍内存分析工具:Memory Monitor、Allocation Tracker和Heap Dump的使用方法。

    1.Memory Monitor

    在Android Studio(以下简称AS)中Android Monitor是一个主窗口,它包含了Logcat,、Memory Monitor、CPU Monitor、 GPU Monitor和Network Monitor。其中Memory Monitor可以轻松地监视应用程序的性能和内存使用情况,以便于找到被分配的对象,定位内存泄漏,并跟踪连接设备中正在使用的内存数量。Memory Monitor可以报告出你的应用程序的内存分配情况, 更形象的呈现出应用程序使用的内存。它的作用如下:

    • 实时显示可用的和分配的Java内存的图表。
    • 实时显示垃圾收集(GC)事件。
    • 启动垃圾收集事件。
    • 快速测试应用程序的缓慢是否与过度的垃圾收集事件有关。
    • 快速测试应用程序崩溃是否与内存耗尽有关。

    1.1 使用Memory Monitor

    在使用Memory Monitor之前要确保手机开启了开发者模式和USB调试。
    使用的步骤为:
    1.运行需要监控的应用程序。
    2.点击AS面板下面的Android图标,并选择Monitors选项。
    如果Memory Monitor已经运行,效果如下图所示(AS版本2.3.2)。
    QQ截图20170705223544.pngQQ截图20170705223544.png

    图中的标注的功能如下:

    • Initiate GC(标识1):用来手动触发GC。
    • Dump Java heap(标识2):保存内存快照。
    • Start/Stop Allocation Tracking(标识3):打开Allocation Tracker工具(后面会介绍)。
    • Free(标识4):当前应用未分配的内存大小。
    • Allocated(标识5):当前应用分配的内存大小。

    图中y轴显示当前应用的分配的内存和未分配的内存大小;x轴表示经过的时间。

    1.2 大内存申请与GC

    am-gc2.pngam-gc2.png

    从上图可以看出,分配的内存急剧上升,这就是大内存分配的场景,我们要判断这是否是合理的分配的内存,是Bitmap还是其他的大数据,并且对这种大数据进行优化,减少内存开销。
    接下来分配的内存出现急剧下降,这表示垃圾收集事件,用来释放内存。

    1.3 内存抖动

    20150924173526554 (1)_副本.jpg20150924173526554 (1)_副本.jpg

    内存抖动一般指在很短的时间内发生了多次内存分配和释放,严重的内存抖动还会导致应用程序卡顿。内存抖动出现原因主要是短时间频繁的创建对象(可能在循环中创建对象),内存为了应对这种情况,也会频繁的进行GC。非并行GC在进行时,其他线程都会被挂起,等待GC操作完成后恢复工作。如果是频繁的GC就会产生大量的暂停时间,这会导致界面绘制时间减少,从而使得多次绘制一帧的时长超过了16ms,产生的现象就是界面卡顿。综合起来就产生了内存抖动,产生了如上图般的锯齿状。

    2.Allocation Tracker

    Allocation Tracker用来跟踪内存分配,它允许你在执行某些操作的同时监视在何处分配对象,了解这些分配使你能够调整与这些操作相关的方法调用,以优化应用程序性能和内存使用。
    Allocation Tracker能够做到如下的事情:

    • 显示代码分配对象类型、大小、分配线程和堆栈跟踪的时间和位置。
    • 通过重复的分配/释放模式帮助识别内存变化。
    • 当与 HPROF Viewer结合使用时,可以帮助你跟踪内存泄漏。例如,如果你在堆上看到一个bitmap对象,你可以使用Allocation Tracker来找到其分配的位置。

    2.1 使用Allocation Tracker

    AS和DDMS中都有Allocation Tracker,这里会·介绍AS中的Allocation Tracke如何使用。首先要确保要确保手机开启了开发者模式,并且开启了USB调试。
    使用的步骤为:
    1.运行需要监控的应用程序。
    2.点击AS面板下面的Android图标,并选择Monitors选项。
    3.点击Start Allocation Tracking按钮,这时Start Allocation Tracking按钮变为了Stop Allocation Tracking按钮。
    4.操作应用程序。
    5.点击Stop Allocation Tracking按钮,结束快照。这时Memory Monitor会显示出捕获快照的期间,如下图所示。
    QQ截图20170708172907.pngQQ截图20170708172907.png

    6.过几秒后就会自动打开一个窗口,显示当前生成的alloc文件的内存数据。

    2.2 alloc文件分析

    自动打开的alloc文件窗口如下图所示。
    QQ截图20170708174544.pngQQ截图20170708174544.png

    该alloc文件显示以下信息:

    说明
    Method 负责分配的Java方法
    Count 分配的实例总数
    Total Size 分配内存的总字节数

    接着我们来分析标红框的内容,负责分配的Java方法为performLaunchActivity,内存分配序列为2369,分配的对象为ActivityThread,分配的实例总数为300个,分配内存的总字节数为10512。不了解performLaunchActivity方法和ActivityThread可以看Android深入四大组件这一系列的文章。

    目前的菜单选项是Group by Method我们也可以选择 Group By Allocator,如下图所示。
    QQ截图20170708192205.pngQQ截图20170708192205.png

    为了更好的解释图中的信息,这里给出测试的代码,MainActivity和SecondActivity 的代码如下所示。
    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private Button button;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button =(Button)findViewById(R.id.bt_next);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(MainActivity.this,SecondActivity.class));
                }
            });
        }
    }
    View Code

    SecondActivity.java

    public class SecondActivity extends AppCompatActivity {
        private static Object inner;
        private Button button;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button = (Button) findViewById(R.id.bt_next);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    createInnerClass();
                    finish();
                }
            });
        }
        void createInnerClass() {
            class InnerClass {
            }
            inner = new InnerClass();
        }
    View Code

    其中SecondActivity是存在内存泄漏的,生成快照期间,我的操作就是在MainActivity和SecondActivity跳转了3次(点击button 共6次)。这时我们回过头来看上图的红框的信息,MainActivity总共分配了3个Intent实例,占用内存为192字节。SecondActivity总共分配了6个实例,占用内存为96字节,其中分配了3个匿名内部类OnClickListener的实例,3个InnerClass的实例。

    我们可以选择列表中的一项,单击鼠标右键,在弹出的菜单中选择jump to the source就可以跳转到对应的源文件中。
    除此之外,还可以点击Show/Hide Chart按钮来显示数据的图形化,如下图所示。

    3.Heap Dump

    Heap Dump的主要功能就是查看不同的数据类型在内存中的使用情况。它可以帮助你找到大对象,也可以通过数据的变化发现内存泄漏。

    3.1 使用Heap Dump

    打开Android Device Monitor工具,在左边Devices列表中选择要查看的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体),在右边选择Heap选项,并点击Cause GC按钮,就会开始显示数据。我们每次点击Cause GC按钮都会强制应用程序进行垃圾回收,并将清理后的数据显示在Heap工具中。如下图所示。

    从上图可以看出,Heap工具共有三个区域,分别是总览视图(标识1)、详情视图(标识2)和内存分配柱状图(标识2)。

    3.2 总览视图

    其中总览视图可以查看整体的内存情况,表中的显示信息如下所示。

    说明
    Heap Size 堆栈分配给该应用程序的内存大小
    Allocated 已分配使用的内存大小
    Free 空闲的内存大小
    %Used 当前Heap的使用率(Allocated/Heap Size)
    #Objects 对象的数量

    结合上表和上图,我们在总览视图获得的信息就是:堆栈分配给当前的应用程序的内存大小为2.346MB,已分配的内存为1.346MB,空闲的内存为1MB,当前Heap的使用率为57.37%,对象的数量为24058个。

    3.3 详情视图

    详细视图展示了所有的数据类型的内存情况,表中列的信息如下所示。

    说明
    Type 数据类型
    Total Size 总共占用的内存大小
    Smallest 将该数据类型的对象从小到大排列,排在第一个的对象所占用的内存
    Largest 将该数据类型的对象从小到大排列,排在最后一个的对象所占用的内存
    Median 将该数据类型的对象从小到大排列,排在中间的对象所占用的内存
    Average 该数据类型的对象所占用内存的平均值

    除了列的信息,还有行信息:

    说明
    free 内存碎片
    data object 对象
    class object
    1-byte array (byte[],boolean[]) 1字节的数组对象
    2-byte array (short[],char[]) 2字节的数组对象
    4-byte array (object[],int[],float[]) 4字节的数组对象
    6-byte array (long[],double[]) 8字节的数组对象
    non-Java object 非Java对象

    行信息中比较重要的是free,它与总览视图中的free的含义不同,它代表内存碎片。当新创建一个对象时,如果碎片内存能容下该对象,则复用碎片内存,否则就会从free空间(总览视图中的free)重新划分内存给这个新对象。free是判断内存碎片化程度的一个重要的指标。
    此外,1-byte array这一行的信息也很重要,因为图片是以byte[]的形式存储在内存中的,如果1-byte array一行的数据过大,则需要检查图片的内存管理了。

    3.4 检测内存泄漏

    Heap Dump也可以检测内存泄漏。在左边Devices列表中选择要查看的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体),在右边选择Heap选项,并点击Cause GC按钮,就会开始显示数据,如下图所示。

    QQ图片20170709204916.png

    这时data object的Total Size为270.266KB。接下来操作应用,这个应用仍旧是在2.2小节所举的内存泄漏的例子,我反复的在MainActivity和SecondActivity跳转了10次(点击Button共20次),数据显示为:
    QQ截图20170709210450.png

    data object的Total Size变为了768.172KB。这时我点击Cause GC按钮,数据显示为:

    QQ截图20170709212646.png

    可以看到data object的Total Size变为了444.516KB,再点击一次Cause GC按钮:
    QQ截图20170709220441.png

    Total Size变为了323.312KB,经过两次Cause GC的操作,Total Size的值从768.172KB变为了323.312KB,这是一个比较大的变化,说明在Cause GC操作之前有462.86KB(768.172KB-323.312KB)的内存没有被回收,可能发生了内存泄漏。

    参考资料
    Memory Monitor
    Allocation Tracker
    Android Monitor Basics
    Android性能专项测试之Memory Monitor工具
    《Android应用性能优化最佳实践》
    《Android群英传 神兵利器》
    《高性能Android应用开发》

  • 相关阅读:
    Android开发 使用 adb logcat 显示 Android 日志
    【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
    C语言 结构体相关 函数 指针 数组
    C语言 命令行参数 函数指针 gdb调试
    C语言 指针数组 多维数组
    Ubuntu 基础操作 基础命令 热键 man手册使用 关机 重启等命令使用
    C语言 内存分配 地址 指针 数组 参数 实例解析
    CRT 环境变量注意事项
    hadoop 输出文件 key val 分隔符
    com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Too many connections
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/9321851.html
Copyright © 2011-2022 走看看