Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用

前言

一个好的性能优化,可以让你的软件运行速度上比别人快,出现的卡顿现象少,而且一个好的性能软件,会在系统内存中生存的更久。性能优化最主要的就是对Java内存的管理,即堆内存中的管理,对于Java内存分配的讲解,详细可见我的博客文章

概念介绍

内存泄漏和内存溢出的区别

  • 内存泄漏:指程序分配出去的内存不再使用,无法进行回收
  • 内存溢出:指程序在申请内存时,没有足够的空间供其使用

成员变量和局部变量内存分配

  • 成员变量中的引用和引用对象存在堆中
  • 局部变量中的引用存在栈中,引用对象存在堆中

工具的说明

  • Android Monitor:用来查看当前设备堆内存的情况,可以通过该工具找到内存泄漏的原因
  • Android Monitor之Memory Usage:经过手动的GC之后,可以查看当前Activity的数量,来判断是否泄漏
  • TraceView:查看某个方法的CPU执行时间和方法运行时间
  • Allocation Tracking:查看内存的饼状图
  • Lint:系统自带的检测工具,可以检测内存泄漏的疑点和书写规范等问题

Android Monitor

我们通过一个单例模式的例子产生的内存泄漏来使用Android Monitor

/**
 * 单例模式
 */
public class CommUtil {
    private static CommUtil instance;
    private Context context;
    
    private CommUtil(Context context) {
        this.context=context;
    }
    public static CommUtil getInstance(Context context){
        if(null==instance){
            instance=new CommUtil(context);
        }
        return instance;
    }
}

/**
 * 使用单例
 */
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CommUtil instance = CommUtil.getInstance(this);
    }
}

接着我们旋转三次屏幕,让Activity不断调用onCreate方法,从而导致CommUtil不断被实例化,在Android Monitor的内存监测中也能看得出来内存在增加

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第1张图片

1、打开Android Monitor

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第2张图片

2、点击Dump java Heap让其产生一份快照

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第3张图片

3、找到MainActivity类,查看其实例情况

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第4张图片

我们可以看到MainActvity有三个实例对象被引用

  • 0号位属于内存泄漏的实例
  • 1号位属于垃圾内存,会被GC回收
  • 2号位属于当前界面引用的实例

这里需要注意,旋转屏幕3次以上都只会有2个MainActivity。当GC回收的时候会将第0个和最后一个留着,其他的都会被回收

4、点击对象实例,找到引用的对象

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第5张图片

5、解决方法

/**
 * 使用单例
 */
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CommUtil instance = CommUtil.getInstance(getApplicationContext());
    }
}

这个时候,我们再按照前面的方法查看MainActivity的实例

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第6张图片

Android Monitor之Memory Usage

正常的情况下,应用程序按返回键退出程序,并且经过多次手动GC,理论上所有的Activity都会被回收,我们可以通过Memory Usage的信息,查看退出程序后的Activity和View是否依然存在内存中,从此可以判断是否发生内存泄漏。我们主要是通过下面的入口,查看到最后程序退出并经过GC后的剩余情况,这里很明显可以看到内存泄漏了Activity和View,正常的值应该是0

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第7张图片

TraceView

我们通过计算斐波拉契列数的例子,学习TraceView的使用

//调用斐波拉契列数
computeFibonacci(40);

public int computeFibonacci(int positionInFibSequence) {
    if (positionInFibSequence <= 2) {
        return 1;
    } else {
        return computeFibonacci(positionInFibSequence - 1)
                + computeFibonacci(positionInFibSequence - 2);
    }
}

1、打开Android Device Monitor (DDMS),按步骤启动TraceView

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第8张图片

2、开始start Method Profiling之后,在主程序中执行斐波拉契列数,等待大概5秒就stop Method Profiling,系统会生成一份分析文件

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第9张图片

3、鼠标移动到某一处黑色的地方

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第10张图片

4、滑动鼠轮进行放大

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第11张图片

5、产看详细信息面板

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第12张图片

列名 作用
Name 该进程中所调用的函数名称
Incl Cpu Time 函数占用的CPU时间,包含内部调用其它函数的CPU时间
Excl Cpu Time 函数占用的CPU时间,但不包含内部调用其它函数所占用的CPU时间
Incl Real Time 函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间
Excl Real Time 函数运行的真实时间(以毫秒为单位),不包含调用其它函数所占用的真实时间
Calls+Recur Calls/Total 函数被调用次数以及递归调用占总调用次数的百分比
Cpu Time/Call 函数调用CPU时间与调用次数的比(该函数平均执行时间)
Real Time/Call 同CPU Time/Call类似,只不过统计单位换成了真实时间

6、解决方法

//将递归换为for循环 同时使用缓存 先将结果缓存起来
public int computeFibonacci(int positionInFibSequence) {
    int prev=0;
    int current=1;
    int values;
    for(int i=0;i

Allocation Tracking

1、打开Android Monitor,找到下面按钮,点击start Allocation Tracking,然后执行我们的程序进行分配内存,最后stop Allocation Tracking,可以看到图片上有一段矩形,就是我们追踪的内存分配部分,等一会会生成一份分析报告

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第13张图片

2、在详细报告中打开图形图,可以看到每一层的内存分布情况,后面的内存分配都可以在图形中结合右边介绍找到

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第14张图片

Lint

1、采用Lint工具系统会检测出一些简单的内存泄漏,或者是书写规范等等问题,使用Analyze里面的Inspect Code

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第15张图片

2、选择整个目录结构即可得到分析报告

Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用_第16张图片

常见的内存泄漏例子

1、静态变量引起的内存泄露情况

当调用getInstance时,如果传入的context是Activity的context,只要这个单例没有被释放,那么这个Activity也不会被释放,一直到进程退出才会释放。解决方法就是将传入的context设置为getApplicationContext()即可

public class CommUtil {
    private static CommUtil instance;
    private Context context;

    private CommUtil(Context context) {
        this.context = context;
    }

    public static CommUtil getInstance(Context mcontext) {
        if (instance == null) {
            instance = new CommUtil(mcontext);
        }
        return instance;
    }
}

2、非静态内部类引起的内存泄露情况

由于非静态内部类对外部类持有应用,且非静态内部类的生命周期和外部类生命周期不一样,就会导致内存泄漏。解决方法就是将非静态内部类设置为静态内部类即可

3、注册的监听未移除引起的内存泄露情况

最常见的是 registerReceiver()、订阅-发布模式

4、资源未关闭引起的内存泄露情况

BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute、attr.recycle()的回收

5、无限循环动画引起的内存泄露情况

没有在onDestroy中停止动画,否则Activity就会变成泄露对象

处理过的内存泄露

1、Handler和Callback内存泄露

在Activity内部创建Handler,由于Activity的生命周期和Handler不一样,所以Handler会内存泄露,解决方法就是将Handler设置为静态类,有时Handler里面会存储Callback等变量,请务必将这些变量设置为弱引用进行存储

2、DCL(双检查锁机制)

DCL的单例需要对单例变量进行volatile修饰,否则JVM会存在指令的重排序优化,导致内存泄露

你可能感兴趣的:(Android进阶——性能优化之Android Monitor、TraceView、Allocation Tracking、Lint的使用)