Android Studido下的应用性能优化总结-内存优化

转载请标明出处(请勿转载删除底部微博、微信等信息):

http://blog.csdn.net/Y1258429182/article/details/51176424

本文出自:杨哲丶的博客

上一篇文章总结的布局优化的问题,如果对布局优化不是很熟悉的,可以看一下Android Studido下的应用性能优化总结–布局优化, 这周一直筹划总结一下内存优化的问题,因为现在对于应用优化的文章很多,但是还是想完善一下才想分享这篇文章的,我会从项目中遇到的一个问题,通过解决问题的过程来分享知识,希望大家有所收获。

“A small leak will sink a great ship.” - Benjamin Franklin

小楼不修补,大船也会翻。——本杰明 富兰克林


出现的问题(What)

  • 场景(Scene): Y君某年某月某日碰到一个奇怪的问题:A页面跳转到B页面,然后跳转到其他页面没有问题,但是在A跳转B,然后B跳转到A页面,就开始发生卡顿,程序也不崩溃!!OMG,这是什么问题,Y君当时确实有点懵逼了,这是肿么一个情况,但是Y君毕竟也是上过刀山下过火海,在bug堆里走出来的人(突然想起了那句话,我要成为海贼王的男人…),扯远了,还是回到问题上来.

分析问题(Why)

由于出现卡顿现象,大部分都出自内存的问题上,所以我们开始分析自己应用的内存使用情况,但是问题来了,用什么工具查看应用的内存使用情况?

因为以前Ecliplse的内存查看略显繁琐,而且Goole推出的新的开发工具Android Studio,有很多我们方便我们安卓开发程序猿调试和分析问题,在这里我就介绍一下Android Studio中的内存分析工具Memory,如果不熟悉的可以按照箭头指示,点击应用相对应的包名,就可以查看该应用内存情况,分析截图如下

  • Y君处理内存还是有稍许经验的,不急不缓的亮出自己珍藏已久的内存分析三剑客:

    1. LeakCanary
    2. Memory Analyzer Tool(MAT)
    3. TraceView
  • 那么我们开始分析问题出在哪里吧 ,Are You Ready? Go!

LeakCanary的使用方法

LeakCanary的GitHub地址

工具介绍:LeakCanary是Square开源的一个内存泄露自动探测神器,它是一个Android和Java的内存泄露检测库,可以大幅度减少了开发中遇到的OOM问题,对于安卓开发者来说,无疑是个福音。

GitHub里边介绍的已经很详细了,我这里也简短的介绍一下使用方法!

在项目的build.gradle文件添加:

 dependencies {
   debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.4-beta2‘
   releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2‘
   testCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2‘
 }

在Application里边初始化

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

当正常接入的情况下,测试应用出现内存不足的时候会接受到通知,截图如下

但是Y君的这个bug出现这种分析,没有接到通知,截图如下

  • Leakcanary是的主要优势就在于自动化过早的发觉内存泄露、配置简单、抓取贴心,缺点在于还存在一些bug,不过正常使用百分之九十情况是OK的,然后我就成了那百分之十,具体问题我也不清楚,但是不妨碍这是一个很好的检查工具。

Memory Analyzer Tool(MAT)的使用方法

MAT独立版内存检测工具下载

工具介绍:MAT(Memory Analyzer Tool)工具在分析 大内存的dump文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用OQL对象查询,以及可以很方便的找出对象GC Roots的相关信息,当然最吸引人的还是能够快速为开发人员生成内存泄露报表,方便定位问题和分析问题。

测试Demo:我写了一个Demo来模拟当时我遇到的情况!让计时器一直发送消息到Handler刷新时间,代码如下

 private void getCurrentTime() {
        //为了测试更加明显
        new Thread() {

            @Override
            public void run() {
                super.run();
                while (flag) {
//                    SystemClock.sleep(1000);
                    long endTime = System.currentTimeMillis();
                    long time = endTime - mStartTime;
                    String result = new SimpleDateFormat("mm:ss").format(new Date(time));
                    Message message = handler.obtainMessage();
                    message.what = MessageConstants.UPDATETIMEFLAG;
                    mFinallyTime = result;//为了跳转携带数据
                    message.obj = result;
                    handler.sendMessage(message);
                }
            }
        }.start();
    }
  • 步骤一:我只讲Android Studio下如何抓取内存使用的情况,点击左上角的箭头提示,会抓取你相对应的位置的内存使用情况,如图4

  • 步骤二:稍等一会,生成的文件会出现在captures中,然后选择文件,点击右键转换成标准的hprof文件,就可以在MAT中打开了。

    再次我解释一下,MAT分两种类型,一种是用于Eclispse插件版,一种是独立版,因为我们用是Android Studio所以只能使用用独立版的MAT.

  • 步骤三:在MAT中打开抓取到的文件后,截图如下

    MAT中提供了非常多的功能,这里我们只要学习几个最常用的就可以了。上图最中央的那个饼状图展示了最大的几个对象所占内存的比例,这张图中提供的内容并不多,我们可以忽略它。

    图片中红色框有使我们经常使用的模块,它们的职责分别是:

  • Histogram可以列出内存中每个对象的名字、数量以及大小。
  • Dominator Tree会将所有内存中的对象按大小进行排序,并且我们可以分析对象之间的引用结构。

MAT中的Dominator Tree 的分析

现在点击Dominator Tree,截图如下:

这张图包含的信息非常多,我来带着大家一起解析一下。首先Retained Heap表示这个对象以及它所持有的其它引用(包括直接和间接)所占的总内存,因此从上图中看,前两行的Retained Heap是最大的,我们分析内存泄漏时,内存最大的对象也是最应该去怀疑的。

另外大家应该可以注意到,在每一行的最左边都有一个文件型的图标,这些图标有的左下角带有一个红色的点,有的则没有。带有红点的对象就表示是可以被GC Roots访问到的,根据上面的讲解,可以被GC Root访问到的对象都是无法被回收的。那么这就说明所有带红色的对象都是泄漏的对象吗?当然不是,因为有些对象系统需要一直使用,本来就不应该被回收。我们可以注意到,上图当中所有带红点的对象最右边都有写一个System Class,说明这是一个由系统管理的对象,并不是由我们自己创建并导致内存泄漏的对象。

那么上图中就无法看出内存泄漏的原因了吗?确实,内存泄漏本来就不是这么容易找出的,我们还需要进一步进行分析。上图当中,除了带有System Class的行之外,最大的就是第一行的MessageQueue对象了,虽然MessageQueue对象现在不能被GC Roots访问到,但不代表着Bitmap所持有的其它引用也不会被GC Roots访问到。现在我们可以对着第二行点击右键 -> Path to GC Roots -> exclude weak references,为什么选择exclude weak references呢?因为弱引用是不会阻止对象被垃圾回收器回收的,所以我们这里直接把它排除掉,结果如下图所示:

最终我们找到了罪魁祸首,开启的很多个线程,while死循环没有停止,我跳转的时候也没把循环关掉,所以有这么多的Thread。

MAT中的Histogram 的分析

首先我们学习一下Histogram的用法,回到Overview界面,点击Histogram,结果如下图所示

这里是把当前应用程序中所有的对象的名字、数量和大小全部都列出来了,需要注意的是,这里的对象都是只有Shallow Heap而没有Retained Heap的,那么Shallow Heap又是什么意思呢?就是当前对象自己所占内存的大小,不包含引用关系的,比如说上图当中,String对象的Shallow Heap最高,说明我们应用程序中用了很多String类型的数据,我们不断的发送Handler不断的创作String对象。可以通过右键 -> List objects -> with incoming references来查看具体是谁在使用这些String,分析如图下,发现然并卵。

那么通过Histogram又怎么去分析内存泄漏的原因呢?当然其实也可以用和Dominator Tree中比较相似的方式,即分析大内存的对象,比如上图中byte[]对象内存占用很高,我们通过分析byte[],最终也是能找到内存泄漏所在的,但是这里我准备使用另外一种更适合Histogram的方式。大家可以看到,Histogram中是可以显示对象的数量的,那么比如说我们现在怀疑MainActivity中有可能存在内存泄漏,就可以在第一行的正则表达式框中搜索“MainActivity”,如下所示:

可以看到,这里将包含“MainActivity”字样的所有对象全部列出了出来,其中第一行就是MainActivity的实例。但是大家有没有注意到,当前内存中是有30个MainActivity的实例的,这太不正常了,通过情况下一个Activity应该只有一个实例才对。其实这些对象就是由于我们刚才不断地横竖屏切换所产生的,因为横竖屏切换一次,Activity就会经历一个重新创建的过程,但是由于LeakClass的存在,之前的Activity又无法被系统回收,那么就出现这种一个Activity存在多个实例的情况了。

接下来对着MainActivity右键 -> List objects -> with incoming references查看具体MainActivity实例,如下图所示

发现跟上边的效果一样,但是对比起来上边的方法比较方便,如果你无法判断是哪一个Acitivity出的问题,你就无法处理了,所以推荐用Dominator Tree分析问题。

总结: 这大概就是MAT工具最常用的一些用法了,当然这里还要提醒大家一句,工具是死的,人是活的,MAT也没有办法保证一定可以将内存泄漏的原因找出来,还是需要我们对程序的代码有足够多的了解,知道有哪些对象是存活的,以及它们存活的原因,然后再结合MAT给出的数据来进行具体的分析,这样才有可能把一些隐藏得很深的问题原因给找出来。

TraceView的使用方法

借鉴了许多关于TraceView的分析,发现都是抄来抄去,最终都是一种解决办法,不是针对各种问题解决问题,针对我这次碰到的这个问题,我慢慢琢磨了这么工具的使用,发现直接可以分析出具体那个方法占用CPU的时间,个人认为从CPU角度思考内存优化,可以考虑使用这个工具来分析。

如何使用打开DDMS,然后如图

先按下Start Method Profiling(箭头1标的位置),然后过一会可以按Stop Method Profiling,然后出现这种跟答题卡一样的,我们只看下部分的分析

显而易见,分析Intel Cpu Time 属性,点击它按照从大到小的排序后,我们清晰发现run()方法居然占了93%的CPU运行内存,并且直接具体到哪一个类里边的方法。剩下的一些属性估计有用,在这里我也就不一一介绍了!如果你想详细了解,点击文章最后的参考链接即可。

解决问题(How)

通过这三种方法分析的分析结果:

1. LeakCanary反馈的信息没有,无法分析

2. Memory Analyzer Tool(MAT)反馈的信息是MainAcitity中创建了很多的Thread,可以直接分析到死循环的毛病

3. TraceView通过CPU的使用情况非常直观的显示出,MainAcitivity中的run()方法占用95%的使用,干净利索的分析出问题的所在

最后解决办法:加1s的sleep时间,A跳转到B页面的时候,关掉循环!!

总结

文章也写到了最后:

如果遇到卡顿问题的时候,推荐分析的步骤:

1. 先用Memory产看内存使用情况

2. 在用LeakCanary看是否能直接分析出那一个类出的错误

3. 如果LeakCanary分析的步骤不够清晰,导出文件用MAT具体分析

4. 如果在MAT你也没有检查出哪里出的错误,可以尝试TraceView来分析内存使用情况,因为他可以具体到哪一个方法。

参考文章:

1. Android最佳性能实践(二)——分析内存的使用情况

2. 正确使用Android性能分析工具——TraceView

3. Android性能优化之被忽视的Memory Leaks



欢迎关注我的微博:

http://weibo.com/u/5345060833

关注微信公众号:YangZheShare

(欢迎关注,最新最实用的技术干货分享)

时间: 2024-10-06 13:11:51

Android Studido下的应用性能优化总结-内存优化的相关文章

Android Studido下的应用性能优化总结--布局优化

前言:一个应用的成功=产品设计*性能 ,再此我们不讨论一个应用的设计,那交给我们可爱又可恨的产品经理和UI设计师来决定!所以这里分步骤讨论如何提升一个应用的性能,这里先探讨布局优化问题. 布局优化 避免过度回执(Overdraw),由于过度绘制会浪费很多的CPU,GPU资源, 检查方法: 通过打开开发者选项–>GPU模式呈现分析–>在屏幕上显示为条形图.然后就会看到如下图的情况,最好用真机,模拟器回执有问题.  你可以观察一下绘制的条形图 tips:每一条柱状线都包含三部分,蓝色代表测量回执D

Android Studio下的应用性能优化总结-内存优化

转载请标明出处(请勿转载删除底部微博.微信等信息): http://blog.csdn.net/Y1258429182/article/details/51176424 本文出自:杨哲丶的博客 上一篇文章总结的布局优化的问题,如果对布局优化不是很熟悉的,可以看一下Android Studido下的应用性能优化总结–布局优化, 这周一直筹划总结一下内存优化的问题,因为现在对于应用优化的文章很多,但是还是想完善一下才想分享这篇文章的,我会从项目中遇到的一个问题,通过解决问题的过程来分享知识,希望大家

App性能优化之内存优化

本文为慕课网<App性能优化之内存优化>课程的学习笔记,视频地址 (http://www.imooc.com/video/13670) ## 如何查看一个app在安卓系统中的内存分配情况? 方法一: 1.启动android studio和虚拟机,建立连接. 2.打开cmd窗口,输入adb shell. 3.输入ps.  4.可以看到有一个name为应用包名的进程,这就是我们的app所在的进程  5.为了具体查看app所在进程的内存使用情况,需输入dumpsys meminfo +包名.  方法

转 iOS和android游戏纹理优化和内存优化(cocos2d-x)

iOS和android游戏纹理优化和内存优化(cocos2d-x) (未完成) 1.2d游戏最占内存的无疑是图片资源. 2.cocos2d-x不同平台读取纹理的机制不同.ios下面使用CGImage,android和windows下是直接调用png库.我测试了下,使用png库直接读取png会比CGImage还要节约1mb左右内存(图片所占内存4mb)但是速度要比CGImage慢一倍.时间和空间如何取舍就看实际情况了.不过最佳的选择似乎是pvr(即使android版本,即使不使用pvrtc4).

Android性能优化(四):内存优化

1.内存的分配策略概述 程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区).堆区和栈区. 静态存储区(方法区):内存在程序编译的时候就已经分配好,这块内存在程序整个运行期间都存在.它主要存放静态数据.全局static数据和常量. 栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限. 堆区:亦称动态

Android性能优化之内存优化

1.内存溢出的原因 1.1.内存泄漏 内存泄漏和内存溢出的区别: 内存泄漏:程序中存在对无用对象的引用,导致GC无法回收.内存泄漏最终会导致oom. 内存溢出:程序在申请内存时,没有足够的内存空间供其使用,出现out of memory. 1.2.保存多个耗用内存过大的对象 应用的某些逻辑操作消耗掉大量内存(譬如加载一张不经过处理的超大超高清图片等)导致超过阈值. 2.内存优化 2.1.bitmap 1)对bitmap进行压缩 通过BitmapFactory.Options设置inSampleS

OS和android游戏纹理优化和内存优化(cocos2d-x)

注:原文地址不详! 1.2d游戏最占内存的无疑是图片资源. 2.cocos2d-x不同平台读取纹理的机制不同. ios以下使用CGImage,android和windows下是直接调用png库.我測试了下.使用png库直接读取png会比CGImage还要节约1mb左右内存(图片所占内存4mb)可是速度要比CGImage慢一倍. 时间和空间怎样取舍就看实际情况了.只是最佳的选择似乎是pvr(即使android版本号.即使不使用pvrtc4). 3.一般来说,我们能够直接使用  w * h * bp

2017版:KVM 性能优化之内存优化

我们说完CPU方面的优化,接着我们继续第二块内容,也就是内存方面的优化.内存方面有以下四个方向去着手: EPT 技术 大页和透明大页 KSM 技术 内存限制 1. EPT技术 EPT也就是扩展页表,这是intel开创的硬件辅助内存虚拟化技术.我们知道内存的使用,是一个逻辑地址跟物理地址转换的过程.虚拟机内部有逻辑地址转成成物理地址的过程,然后再跳出来,虚拟机这块内存又跟宿主机存在逻辑到物理的转换.有了EPT技术,那么能够将虚拟机的物理地址直接翻译为宿主机的物理地址,从而把后面那个转换过程去掉了,

嵌入式/X86下linux系统死机及内存优化

一.  CPU 过高或死锁导致系统卡死 1. CPU占用过高 (1)开线程太多导致CPU占用过高,系统卡死 解决:优化应用层业务逻辑,有些业务不必开线程就不开 (2)频繁清缓存导致读spi-flash频繁,引起CPU过高 解决:在内核中文件系统部分进行处理,不去清缓存,然后在应用层用后台服务进程清缓存.这样可能导致系统内存不够用,这样就需要再对系统内存进行优化 2. CPU死锁 (1)Spinlock+死等导致死锁 (2)应用层pthread_mutex_lock死锁 3.  CPU过热导致系统