专栏首页7号代码Android应用性能优化——内存优化(内附一个内存泄露优化实例)

Android应用性能优化——内存优化(内附一个内存泄露优化实例)

当我们刚开始接触Android时,可能关注的比较多的是如何实现某个功能,但学到一定程度的时候,我们会发现无论一个应用多么炫酷,如果运行特别慢,或者说很耗内存,这将会带来很差的用户体验,所以说,性能优化变得尤为重要。

一. 垃圾回收机制


自动管理内存和回收机制,垃圾回收器负责回收程序中已经不使用,但是仍然被各种对象占用的内存,将程序员从繁重、危险的内存管理工中解放出来。

缺点:可能会占用大量资源。

Android有垃圾回收机制,无需手动管理内存,Android系统会自动跟踪所有对象,并释放那些不再使用的对象。

二. Android中的垃圾回收机制


新生代

  • 大多数新建的对象都位于Eden区。
  • 当Eden区域被对象填满时,就会执行Minor GC,并把所有存活下来的对象转移到其中一个survivor区。
  • Survivor Space:S0、S1有两个,存放每次垃圾回收后存活的对象。
  • Minor GC同样会检查survivor区存活下来的对象,并把它们转移到另一个survivor区,这样在一段时间内总是有一个空的survivor区。

老年代

  • 存放长期存活的对象和经过多次Minor GC后,依然存活下来的对象。
  • 满了进行Major GC。

永久代

  • 存放方法区,方法区中有要加载的类信息、静态变量、final类型的常量、属性和方法信息。

三. 内存泄露


  • 应用程序分配了大量不能被回收的对象。
  • 系统可分配内存越来越少。
  • 新对象的创建需要内存不够。
  • GC之后再分配。
  • 60fps。

四. 内存抖动


因为在短时间内大量的对象被创建又马上被释放,瞬间产生大量的对象会严重占用新生代的内存区域,当达到阈值,剩余空间不够的时候,会触发GC从而导致刚产生的对象又很快被回收,即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC,这个操作又可能会影响到帧率,并使得用户感知到性能问题。

五. 工具


Memory Monitor

蓝色部分表示使用内存,灰色部分表示空闲内存,峰值表示发生了一次垃圾回收。

特点:

  • 方便显示内存使用和GC情况。
  • 快速定位卡顿是否和GC有关。
  • 快速定位Crash是否和内存占用过高有关。
  • 快速定位潜在的内存泄露问题。
  • 简单易用。
  • 不能准确定位问题。

Allocation Tracker

跟踪对象内存分配的工具。可以追踪应用程序在运行时所有已分配的内存,所有已创建的对象,对象的数量和他们所占用的内存大小以及这些对象是在哪些方法中创建的,用于检测内存抖动现象。

特点:

  • 定位代码中分配的对象的类型、大小、时间、线程、堆栈等信息。
  • 定位内存抖动问题。
  • 配合Heap Viewer一起定位内存抖动问题。
  • 使用复杂。

Heap Viewer

实时展示应用程序运行时所有已分配的对象的数量、大小以及类型信息。用于检测内存泄露。

特点:

  • 内存快照信息。
  • 每次GC之后收集一次信息。
  • 查找内存泄露利器。
  • 使用复杂。

六. 实例


这里有一个存在内存泄露的例子,下载地址:https://github.com/lzyzsd/MemoryBugs

主要使用MemoryMonitor, AllocationTracker,HeapDump以及LeakCanary等工具来查找潜在的内存问题,并尝试解决。

解决过程记录如下:

运行该程序,可以看到主界面如下图所示:

主界面

有一个TextView,一个半圆,两个按钮。

这里先点击第一个按钮StartActivityB,这时会弹出一个Toast:请注意查看通知栏LeakMemory,点开通知栏的通知,看到有提示MainActivity has leaked,意思就是MainActivity出现内存泄露,如下图:

MainActivity has leaked 1

通过分析,是由于static类型的sTextView引用了mContext导致了MainActivity发生了内存泄漏,看到这里很多人估计会一脸懵逼,难道手机会自带检测内存泄露的工具吗?其实不是,看程序源代码,不难发现在build.gradle中引入了一个叫LeakCanary的工具,具体代码如下:

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1

并且在MyApplication中LeakCanary.install(this);

由于static类型的变量是不会被垃圾回收的,所以导致了MainActivity的内存泄露,解决方案就是去掉static,修改代码:

//    private static TextView sTextView;    
      private TextView mTextView;

接着看一下半圆的绘制是否存在问题,先看代码:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rect = new RectF(0, 0, 100, 100);
    Paint paint = new Paint();
    paint.setColor(Color.RED);
    paint.setStrokeWidth(4);
    canvas.drawArc(rect, 0, 180, true, paint);
}

果然有问题,由于onDraw()方法调用比较频繁,所以一般尽量避免在onDraw()方法中创建对象,这里恰恰就在onDraw()方法中创建对象,所以这里的修改方案是把创建对象放到定义成员变量的位置。代码如下:

private RectF mRectF = new RectF(0, 0, 100, 100);
private Paint mPaint = new Paint();

这时的onDraw()方法如下:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(Color.RED);
    mPaint.setStrokeWidth(4);
    canvas.drawArc(mRect, 0, 180, true, mPaint);
}

OK,再次运行程序,点击按钮StartActivityB,没有出现LeakCanary的提示。

在Android Studio中打开Android Monitor -> Memory,不断点击按钮StartAllocation,不断的发生内存回收和分配,会出现以下状况,这就是我们上边所说的内存抖动。

内存抖动

配合Allocation Tracking,在内存抖动开始时点击Start Allocation Tracking按钮,在抖动结束后再点击一下。会得到如下图所示的.alloc文件:

Group by Method

选择Group by Allocator,然后点击最外圈的绿色,然后双击右面的Activity,把Activity展开后会发现进行很多Rect和StringBuilder对象的创建。

Group by Allocator

问题就在这里,看代码:

private void startAllocationLargeNumbersOfObjects() {    
Toast.makeText(this, "请注意查看MemoryMonitor 以及AllocationTracker", Toast.LENGTH_SHORT).show();
    for (int i = 0; i < 10000; i++) {
        Rect rect = new Rect(0, 0, 100, 100);
        System.out.println("-------: " + rect.width());
    }
}

可以看到在for循环中一直创建对象及字符串的拼接。

修改方案是把Rect对象的创建放到成员变量中,在onCreate中进行初始化,为了避免在logcat输出时产生大量的String对象,修改方案是在onCreate中把String对象创建好,这样就不会重复创建了,还要把里面的字符串提取出来,放到strings.xml中,有的要设置为static final类型的字符串资源,修改代码如下:

成员变量:

public static final String LINE_TAG = "-------: ";
private Rect mRect;
private String mLogString;

onCreate():

mRect = new Rect(0, 0, 100, 100);
mLogString = LINE_TAG + mRect.width();

startAllocationLargeNumbersOfObjects()

private void startAllocationLargeNumbersOfObjects() {
    Toast.makeText(this, R.string.memory_monitor, Toast.LENGTH_SHORT).show();
    for (int i = 0; i < 10000; i++) {
        System.out.println(mLogString);
    }
}

strings.xml:

<resources>
    <string name="app_name">MemoryBugs</string>
    <string name="memory_monitor">请注意查看MemoryMonitor 以及AllocationTracker</string>
    <string name="leakmemory">请注意查看通知栏LeakMemory</string>
    <string name="hello_world">Hello World!</string>
</resources>

以上解决了三个问题,那么怎么检测是否还存在内存泄露呢?还有一个工具叫Heap Viewer,这个工具可以实时展示应用程序运行时所有已分配的对象的数量、大小以及类型信息,可以检测内存泄露。

在手机屏幕上点击StartActivityB,在Android Studio中点击Dump Java Heap,选择Package Tree View,找到我们的程序,可以看到MainActivity还没有被垃圾回收。

Heap Viewer.png

手动进行一下垃圾回收,再次点击Dump Java Heap,可以看到如下效果:

GC之后_Heap Viewer.png

这时看到MainActivity已经被垃圾回收了,不存在内存泄漏问题了。

参考:http://www.jianshu.com/p/c53101db112e

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android开发高级进阶——Service与Notification

    Service是处理一些后台任务,在主线程中,并不能执行耗时操作。而Thread是开启一个子线程,在子线程中执行耗时操作,这样不会阻塞主线程。

    trampcr
  • Android开发高级进阶——多进程间通信

    当一个应用在开始运行时,系统会为它创建一个进程,一个应用默认只有一个进程,这个进程(主进程)的名称就是应用的包名。

    trampcr
  • 面向对象设计原则

    面向对象设计的目标之一在于支持可维持性复用,一方面需要实现设计方案或者源代码的复用,另一方面要确保系统能够易于扩展和修改,具有较好的灵活性。

    trampcr
  • win32应用程序性能测试-内存篇

    本文主要讲述windows平台下应用程序性能测试的内存相关的知识,通过本文了解内存基本原理和分析内存占用问题。 一、内存是什么? 1内存分为物理内存和虚拟内存 ...

    腾讯移动品质中心TMQ
  • rt-thread的内存管理分析

    内存是计算机中十分重要的资源。随着芯片性能的提升,容量的变大,内存资源的管理显得非常重要。内存管理是操作系统中一个基本功能,一般操作系统的功能可以概括为五个部分...

    bigmagic
  • 《深入理解 Java 虚拟机》学习 -- Java 内存模型

    在硬件中,为了解决处理器与内存的速度矛盾,在两者之间使用了高速缓存,但也引入了新的问题:缓存一致性。

    希希里之海
  • 什么是物理/虚拟/共享内存——Linux内存管理小结一

    提到内存,我们会想到经常接触的三个词:虚拟内存、物理内存、共享内存。它们分别对应top输出中的VIRT、RES、SHR三列。

    用户5807183
  • Apache Spark 内存管理详解(上)

    本文旨在梳理出Spark内存管理的脉络,抛砖引玉,引出读者对这个话题的深入探讨。本文中阐述的原理基于Spark 2.1版本,阅读本文需要读者有一定的Spark和...

    大数据技术架构
  • 内存溢出和内存泄漏

    通俗的讲就是设备内存不够了。就好比我们的手机,运行内存是4G的,当我们运行了太多的程序时,在运行其他的软件时就会很卡或者提示xx运行停止。

    卡二条的技术圈子
  • 聊一聊内存管理(一)

    在我们的日常生活中,经常会遇到这样的对话。当电脑运行程序变得很慢很卡的时候,就会听到身边的朋友建议我们去增加电脑的内存。这是为什么呢?内存在计算机体系结构中起了...

    算法与编程之美

扫码关注云+社区

领取腾讯云代金券