Android性能优化之Android Device Monitor使用

本文介绍了Android内存的构成,探讨了垃圾回收机制和内存泄漏的概念及影响。通过Android Device Monitor,详细阐述了如何检测和分析内存泄漏,包括使用步骤和界面解读,以一个内存泄漏的实例深入剖析了问题原因,并提出了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是内存

Android系统为我们APP分配的内存大小是有限的,不同的手机型号、不同的ROM分配的内存大小不一定一样,这里所提到的内存一般是指Android手机的RAM,RAM包含寄存器、堆、栈、静态存储区域、常量池。通常我们所说的Android内存泄漏中的内存指的是其中的堆内存空间,一般来说我们new出来的对象都会存储在堆内存中,这部分内存是由GC进行回收管理的。

垃圾回收机制

垃圾回收器(garbage collection简称GC)可以自动清空堆中不再使用的对象,不用我们手动释放内存,提高了开发效率。在JAVA中对象是通过引用进行使用的,如果没有引用指向该对象,不存在从GC根节点到该对象的引用链,这样的对象称为不可到达(unreachable),那么对于GC来说这个对象就是需要被回收的;反之该对象是从根节点可到达的,那么这个对象就不会被GC回收。

什么是内存泄漏

如果一个无用对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间的浪费,这种情况就是内存泄露。

内存泄漏的影响

导致可用内存越来越少,最终可能会发生OOM。
可用内存减少会导致GC被触发,如果频繁触发GC会影响性能造成程序卡顿。

性能优化分析工具

开发过程中应该都会遇到程序性能方面的问题,性能优化的方式有很多种,但是首先需要我们定位是哪一块造成的问题,Android Studio为我们提供了性能分析工具Android Device Monitor。

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

public class UserInfoManage {

    private static UserInfoManage instance = null;
    private Context context;

    public UserInfoManage(Context context) {
        this.context = context;
    }

    public static synchronized UserInfoManage getInstance(Context context) {
        if (instance == null) {
            instance = new UserInfoManage(context);
        }
        return instance;
    }
}

上述代码就是一个简单的单例模式泄露的场景,如何将代码里面的内存泄露给找出来?我们先来分析一下上面代码是如何发生内存泄露的。

我们知道内存泄漏产生的原因是:当一个对象已经不再使用本该被回收时,有另外一个正在使用的对象持有它的引用,从而使对象不能被回收,这种导致了本该被回收的对象不能被回收而停留在堆内存中的情况,就是产生了内存泄漏。

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

1、在Android Studio中通过Tools -> Android -> Android Device Monitor打开。

Monitor

2、在打开的界面中首先选中你要分析应用程序的包名,旋转手机进行横竖屏切换,然后点击Update Heap来更新统计信息,接着点击Cause GC制造GC操作即可查看当前堆内存的占用情况,最后点击Dump HPROF file即当前应用的内存信息保存成hprof文件可另存至指定目录下。

hprof

3、在Android Studio中将刚才保存的hprof文件拖动到编写代码的窗口,就出现下面的显示信息。

hprof

Hprof Viewer主要分ABC三大区域:
区域A:这个应用中所有类的名字。
区域B:左边类的所有实例。
区域C:在选择B中的实例后,这个实例的引用树。

上图中A区域:

左上角是Hprof Viewer查看方式的可选列表,分别是用来选择Heap区域和Class View的展示方式的。

Heap类型分为:
App Heap – 当前App使用的Heap。
Image Heap – 磁盘上当前App的内存映射拷贝。
Zygote Heap – Zygote进程Heap(每个App进程都是从Zygote孵化出来的,这部分基本是Framework中通用类的Heap)。

Class View类型分为:
Class List View – 类列表方式。
Package Tree View – 根据包结构的树状显示。

A区域左上角列名解释

Class Name 类名,Heap中的所有Class
Total Count 内存中该类这个对象总共的数量,有的在栈中,有的在堆中
Heap Count 堆内存中这个类对象的个数
Sizeof 每个该实例占用的内存大小
Shallow Size 所有该类的实例占用的内存大小
Retained Size 所有该类对象被释放掉,会释放多少内存

B区域右上角列名解释

Instance 该类的实例
Depth 深度,从任一GC Root点到该实例的最短跳数
Dominating Size 该实例可支配的内存大小

C区域描述的是B中实例具体被引用的信息

B区域右上角有个“三角”的按钮,点击会进入Hprof Analyzer的hprof分析界面,在这个界面中可以直接将可能导致内存泄露的类找出来。

hprof

我们来分析一下MainActivity的泄露情况:

  • 一个Activity应该只有一个实例,但是从A区域来看Total Count的值为2,Heap Count的值也为2,这说明有一个实例是多余的。
  • 在B区域中可以看到两个MainActivity的实例,点击一个查看它的引用树情况。
  • 在C区域中可以看到MainActivity的实例Context被UserInfoManage的instance引用了,引用深度为1。
  • 在Analyzer Tasks区域中可以看到Leaked Activities中包含了MainActivity。

以上分析的结果表明MainActivity发生了内存泄露。

解决方案:

public class UserInfoManage {

    private static UserInfoManage instance = null;
    private Context context;

    public UserInfoManage(Context context) {
        this.context = context;
    }

    public static synchronized UserInfoManage getInstance(Context context) {
        if (instance == null) {
            instance = new UserInfoManage(context.getApplicationContext());
        }
        return instance;
    }
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值