你的位置:首页 > 操作系统

[操作系统]android 性能分析案例


    本章以实际案例分析在android开发中,性能方面的优化和处理。设计到知识点有弱引用,memory monitor,Allocation Tracker和leakcanary插件。

1.测试demo

    下载bug项目:https://github.com/lzyzsd/MemoryBugs,请注意配合使用MemoryMonitor, AllocationTracker以及HeapDump,LeakCanary等工具来查找潜在的内存问题,并尝试解决。

2.测试工具介绍

(1)memory monitor简介

    AndroidStudio提供了Memory Monitor来实时显示应用运行时内存占用情况,下边蓝色部分是现在占用的内存,上面灰色的部分显示是已回收的内存。如果在图上看到尖峰,也就是快速分配内存又被回收,也就是发生了内存抖动,这里就是需要优化的地方。在模拟器或者真机中Debug项目,可实时监看Memory,CPU,NetWork等的资源占用情况。

    单击Allocation tracker标签,就会打开一个新的窗口,单击“Start Tracing”按钮;然后,让应用运行你想分析的代码。运行完毕后,单击“Get Allocations”按钮,一个已分配对象的列表就会出现第一个表格中。

单击第一个表格中的任何一项,在表格二中就会出现导致该内存分配的栈跟踪信息。通过allocation tracker,不仅知道分配了哪类对象,还可以知道在哪个线程、哪个类、哪个文件的哪一行。查看方式如下:

    圆心是起点处,如果你把鼠标放到我图中标注的区域,会在右边显示当前指示的是什么线程(Thread1)以及具体信息(分配了8821次,分配了 564.18k的内存),但是红框标注的区域并不代表Thread1,而是第一个同心圆中占比最大的那个线程,所以现在把鼠标放到第一个同心圆上,可 以看出来,划过同心圆的轨迹时可以看到右边的树枝的变化情况。

3.案例分析

(1)内存波动

    内存波动指不合理的设计,在一瞬间创建了多个对象,有及时释放时产生影响内存性能的现象。案例中的问题代码如下:

    执行代码引起内存波动的分析图如下:

(2)线程致使内存泄露

    Android中使用Handler造成内存泄露的例子代码如下:

1 Handler mHandler = new Handler() {2   @Override3   public void handleMessage(Message msg) {4     mImageView.setImageBitmap(mBitmap);5   }6 }

 泄露原因分析

    上面是一段简单的Handler的使用。当使用内部类(包括匿名类)来创建 Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然就不可能通过Handler来操作 Activity中的View)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如 图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条 Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

内存泄露的危害
    内存泄露会出现虚拟机占用内存过高的危害,导致OOM(内存溢出),程序出错。对于Android应用来说,用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,致使程序崩溃。使用Handler导致内存泄露的解决方法如下:

  • 通过程序逻辑来进行保护

    1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
    2.如果Handler是被delay的Message持有了引用,使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

  • 将Handler声明为静态类

    静态类不持有外部类的对象,所以Activity可以随意被回收。代码如下:

1 static class MyHandler extends Handler {2   @Override3   public void handleMessage(Message msg) {4     mImageView.setImageBitmap(mBitmap);5   }6 }

     声明静态类后,由于Handler不再持有外部类对象的引用,导致程序不允许该Handler去操作Activity中的对象了。解决方法是在Handler中增加一个对Activity的弱引用(WeakReference)。参考代码如下:

 1 static class MyHandler extends Handler { 2   WeakReference<Activity > mActivityReference; 3   MyHandler(Activity activity) { 4     mActivityReference= new WeakReference<Activity>(activity); 5   } 6  7   @Override 8   public void handleMessage(Message msg) { 9     final Activity activity = mActivityReference.get();10     if (activity != null) {11       mImageView.setImageBitmap(mBitmap);12     }13   }14 15 }

WeakReference简介
    WeakReference 弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不避免了。

    案例中存在hander内存泄露的代码截图如下:

    通过上一章介绍的leakcanary插件,可检查到内存泄露相关的代码,leakcanary检查运行截图如下:

去澳大利亚签证要几天办理澳大利亚签证费用出发去澳大利亚旅游澳大利亚旅游报价深圳到新西兰旅游南海影视城穿旗袍免费活动?2015佛山南海影视城门票免费到什么时候? 南海影视城3月有什么活动?2015佛山南海影视城门票免费时间? 2015清明节下川岛门票价格?台山下川岛清明节好玩优惠活动? 台山下川岛特产有什么好吃的?上下川岛旅游美食介绍? 天津有什么风味小吃? 白金湾有什么好吃的台湾小吃?台湾士林美食节上的天津白金湾 下雪了 和Ta来一场风花雪月的故事吧 阿庐古洞好玩吗?怎么去? 九寨沟迎客松 峨眉山昔日十景 平乐古镇交通图 峨眉山泡温泉常识 令人着迷的泰国 吃喝玩乐奇幻全体验 古蔺菜花秀出画里乡村 林中有机茶“峨眉雪芽”成新宠 关于加强监督管理规范旅游市场秩序的工作意见 550-5105F Datasheet 550-5105F Datasheet 550-5107 Datasheet 550-5107 Datasheet 550-5107-002 Datasheet 550-5107-002 Datasheet 俄罗斯旅游团 俄罗斯旅游团 俄罗斯旅游团 去俄罗斯旅游行程 去俄罗斯旅游行程 去俄罗斯旅游行程 预定俄罗斯旅游 预定俄罗斯旅游 预定俄罗斯旅游