转-Android studio Android Monitor介绍

 

Android studio Android Monitor介绍一


摘要

Android Monitor 很强大,有5个模块,Memory、Network、logcat、CPU、GPU。 现在分5篇文章分别介绍。

一、Memory

这个模块包括4个功能,分别是Enabled,Initiate GC,Dump Heap JAVA,Start Allocation Tracking.。对应面板的上的4个按钮:

1.Enabled

Memory的开关。如果选择关闭,则不对当前进程进行内存监测。

2.Initiate GC

手动调用GC,我们在抓内存前,一定要手动点击 Initiate GC按钮手动触发GC,这样抓到的内存使用情况就是不包括Unreachable对象的.

3.Dump Heap java

点击这个按钮后,就在你点击的时刻,获取hprof文件(hprof文件是我们使用MAT工具分析内存时使用的文件),但这里直接产生的文件MAT还不能直接使用,需用转换成标准的hprof文件。可以使用AndroidStudio转换或者用hprof-conv命令转化,具体不详细介绍,网上可以查到.


此刻分析数据比较简单,需要详细分析那么就得使用MAT工具,android studio 1.5还没集成MAT,相信之后的版本会集成。之后的文章我会专门写一篇MAT的使用文章。虽然简单,但不是一点价值都没有。我们用一个案例来说明一下。

先看截图:

在A区域(当前是类视图,即使class list view,还有包视图)点击Retained Size(包括直接引用的和间接引用的内存)排名,点击byte[],在B区域即使A中选中类的对象,点击Shallow size 根据这些对被直接引用的大小排名。选一个,然后在C中展现,这个对象的引用链,看到是被bitmap_unselect引用,占用大小是 57892,bitmap_unselect又是被zoomViewLeft引用Depth就是被引用的层次。我们就check一下ZoomView的代 码。

类有300行,我们截图相关的代码:

可以看到这个图片始终没有被回收,所以一直占用内存。同理看到这个类里面还有其他bitmap,虽然不造成严重问题,但是内存优化的地方,如果不会 被使用就要及时释放。我们试着优化一下,经过分析,bitmap_unselect就使用一次,在canvas.drawBitmap()之后我们就释放 这个bitmap,

canvas.drawBitmap(bitmap_unselect, centerX - bitmap_unselect.getWidth()/2,
      0 - bitmap_unselect.getHeight()/2null);
bitmap_unselect.recycle();
bitmap_unselect = null;

再用同样的方法生成hrpof,在B,C区域发现已经找不到bitmap_unselect,说明这部分内存已经被释放掉。举这个例子不是说明可以 替代MAT,相反是一种补充,MAT分析复杂,但精准。而这个简单,粗暴,但也奏效。在开发过程中可以配合使用这个轻量的工具。

4.Start Allocaton Tracking

开始分配追踪,第一次点击可以指定追踪内存的开始位置,第二次点击可以结束追踪的位置。这样我们截取了一段要分析的内存,等待几秒钟AndroidStudio会给我们打开一个Allocation视图.

主要分析了各个线程中的方法所占用内存的大小。就以分析UI线程为例。

依 次点开所占用内存最大的方法,可以看到我们跟到了Choregrapher的doFrame,读过源码的同学知道,这个方法是绘制界面的开始,这个方法会 调用view树的onlayout,onDraw等。就不往下点开了。同理我们app中要是有其他线程,也可以照此分析,找到占用内存最大的方法。具体案 例参考此文:http://blog.csdn.net/editor1994/article/details/50394560。

 

 

使用AndroidStudio分析和解决ImageLoader引起内存泄露问题

标签: 内存泄露Android
2473人阅读 评论(19) 收藏 举报
分类:

大家好!本人是一名刚刚实习5个月的实习生。受益CSDN有一年的时间,经常看大神们写的博客学到很多东西。最近在公司做项目时收获一些东西希望能帮到需要的人。第一次写博客希望大家多多包涵,提出批评与指导。

言归正传!

这篇博文主要介绍使用AndroidStudio对内存进行分析和跟踪,还有就是从源码角度解决ImageLoader引起的OOM问题。

公司正在做的项目使用到了ImageLoader来加载大量图片,我也是第一次使用,就拿来直接用了。写完的代码运行很正常的加载图片,并没有发现 什么问题。但是软件拿给测试部门测试的时候发现了问题。当多次打开软件中一个游览大图片的Activity(每次游览的图片都不一样)后,这时在这个 Activity中点击返回后,屏幕会突然变黑,然后回到了软件的MainActivity,并没有回到上一级Activity,甚至连之间打开的多个 Activity都没有返回。当自己亲自测试了这个Bug后,想了一下问题怀疑可能是重启了这个应用。但那个游览大图片的Activity开始打开的几次 都可以正常的回到上一级Activity,为什么再多打开几次就不能正常回退了呢!这时候我就开始看LogCat打出的日志,看看有没有发生什么异常。发 现ImageLoader产生了OOM的Bug。如下图:

OOM

然后上网上查了一下ImageLoader引起内存泄露的问题,发现有很多人遇到,但说到解决的办法的,并没有很好的答案。大部分都是说 ImageLoaderConfiguration.Builder(context)和DisplayImageOptions.Builder()里 面的配置问题,但并不能解决这个问题。还有人说换一个图片加载的框架,像Universal-Image-Loader(UIL)和Google的 Volley框架,我尝试换了Universal-Image-Loader(UIL)这个,但发现还是有这个问题。而且项目已经到后期,换一个框架改动 比较大,不太现实,就没有尝试Volley框架。还有人说是应为使用了cacheInMemory(true)和cacheOnDisc(true)这两 个属性导致的,但是我将他们设为false还是会引发OOM,所以就自己用工具分析。
既然产生了OOM,我们就要分析内存,看看具体是什么原因导致的。正好最近刚刚开始使用Android Studio工具(使用过后真的会上瘾,发现很多地方都比eclipse强大),在使用AndroidStudio的Memory工具观察的时候发现了问 题,在我多次打开这个Activity的时候,发现Memory在一直的增长,每次Activity退出后Memory也没有下降。

内存上升图

就这样当我多次游览不同的图片,多次打开这个Activity后,这个Memory就一直的增长,当增长到120+M的时候应用突然挂掉,之后又自 动重启。发现了这个现象我们就可以借助AndroidStudio的强大工具来分析导致这个问题的原因。在Memory窗口的左边有四个按钮,分别是:
Enabled(蓝色的开关):就是一个正常的开关功能
Initiate GC(橙色小卡车):就是手动调用GC,我们在抓内存前,一定要手动点击 Initiate GC按钮手动触发GC,这样抓到的内存使用情况就是不包括Unreachable对象的(Unreachable指的是可以被垃圾回收器回收的对象,但是 由于没有GC发生,所以没有释放,这时抓的内存使用中的Unreachable就是这些对象)
Dump Java Heap(紫色带向下的箭头):获取hprof文件(hprof文件是我们使用MAT工具分析内存时使用的文件),但这里直接产生的文件MAT还不能直接 使用,需用转换成标准的hprof文件。可以使用AndroidStudio转换或者用hprof-conv命令转化,具体不详细介绍,网上可以查到。
Start Allocation Tracking(紫色带圆圈):开始分配追踪,第一次点击可以指定追踪内存的开始位置,第二次点击可以结束追踪的位置。这样我们截取了一段要分析的内 存,等待几秒钟AndroidStudio会给我们打开一个Allocation视图(感觉和MAT工具差不多,不过MAT工具更加强大,我们也可以获取 hprof文件,使用MAT来分析)如下图:

Start Allocation Tracking

我截取了这段内存,接下来我们就看Allocation视图来分析。

Thread

通过视图中的内容我们首先看到23号线程它占用了百分之40的内存,那么我们就点开它看一下,每次都只要点开其中内存占用最大的就可以。

OpenThread

到划红线的那个位置就可以了,这里已经到了安卓的android.graphics源码的层次,所以我们不需要再向下看了。我们看一下红线上面的ImageLoader里的那个decodeStream方法:
decodeStream

它里面就是调用了BitmapFactory.decoceStream()方法然后返回了一个Bitmap对象。基本上已经可以确定占用如此多内 存的就是Bitmap对象,那么我们只要将这个Bitmap释放掉就可以了。那么就接着分析看看在什么地方释放,我们看一下最上面的 ImageLoader.core包下的LoadAndDisplayImageTask类的run()方法,发现里面有一个Bitmap对象,他是调用 了内部tryLoadBitmap()方法给赋值的,而这个方法最后其实就是我们上面看到的调用的decodeStream方法返回的Bitmap。应为 这之间的Bitmap都是方法里的局部变量所以我们不用考虑这些Bitmap的释放,那就接着向下看,发现这个Bitmap被传到了 DisplayBitmapTask这个类中,那么我们再进入到这个类中看发现了一个惊喜:

DisplayBitmapTask

这个对象被传到了DisplayBitmapTask的成员变量中,DisplayBitmapTask本身也实现了Runnable接口,那么我 就想是不是应为DisplayBitmapTask这个类没有及时回收导致的Bitmap成员变量占用内存的问题,所以我就在run()方法跑完之后,让 Bitmap = null。然后又将程序运行了一遍,但是发现根本没有解决这个问题。这时候又重头想了一下整个过程,发现我的操作是不对的,虽然我成功将 DisplayBitmapTask中的Bitmap成员变量置为了空,但是他只是真正的Bitmap内存的一个引用,我并没有将真正的Bitmap内存 空间所释放,只是将他的一个引用给释放了。通过网上查询发现,Bitmap会保存在C层的内存中,如果我们想要释放他在C层中的内存,可以调用 Bitmap的recycle()方法,从代码中看,这个方法本身会调用Bitmap.cpp中的Bitmap_recycle方法,这个是native 方法。这样就可以通知底层将C层中的Bitmap内存释放掉,而且还会将一些相关的引用计数置0。但是并不会立即释放掉,这要看系统。 一下子完美解决。既然查到了原因,那就开始实践,我将Bitmap = null这句话改为Bitmap.recycle()。好了,接下来我再次运行,发现这回应用直接就崩了,汗!但是可以接受,只要能在Logcat中抓到 Log,一切问题都好解决。看了一下log发现下面这个Error:

error

通过字面理解的意思就是,我们的View去显示了已经recycle的Bitmap。仔细想一想确实是,我们在ImageLoader中的一个线程 的run方法中直接将Bitmap释放掉了,那我们的应用岂不是直接显示一个空的Bitmap,这当然会引起错误。既然不能在这里释放掉Bitmap,那 我们应该在什么地方释放呢?那一定是在我们的View不再使用这个Bitmap的时候调用,想一想,那我们只要在Activity或者fragment的 onDestory()方法中释放了不就可以了吗?但这时又有一个问题产生,我们该如何在上层应用获取到这个View用到的Bitmap的对象呢?想了半 天发现好像只能通过改写ImageLoader的源码来想办法将这个对象会传到上层应用。正当我在ImageLoader中寻找在哪里写这个回调方法时, 突然我看到了这段代码:

这里写图片描述

发现其实ImageLoader的源码中已经为我们写好了这个回调监听器接口,而且在ImageLoader的displayImage方法中,也 重载了一个提供了传递ImageLoaderListener实现对象的方法。这样我们只需要实现ImageLoaderListener这个接口,并实 现他的onLoadingComplete方法,在这个方法中对Bitmap做处理就可以了。

这里写图片描述

我们只需要在Activity或Fragment的onDestory()方法中再调用cleanBitmapList()方法就可以了。通过上面的更改我再次运行应用,发现真的解决了这个问题,我的应用内存不再会是一直的上升,而是上升之后又会下降。如图:

这里写图片描述

注:这个方式比较适合cacheInMemory(false)和cacheOnDisc(false)的情况写,应为如果你将这两个设置为 true时,那么下次再次加载图片时他会从内存和硬盘中去加载图片,这两个地方也持有Bitmap的引用,这时就会再次出现trying to use a recycled bitmap 这个错误。当然你也是可以设置为true的,只要在清除Bitmap(也就是我们这里调用cleanBitmapList)的地方再加上 mImageLoader.clearDiscCache()和mImageLoader.clearMemoryCache()这两句话就可以。

好了,整个ImageLoader引起的OOM问题额分析与解决的过程就是这些,希望能对需要的人有帮助,通过这个问题我也学会到,遇到问题最好的 解决办法就是我们要从最根本源码的角度去分析,这样既能学到很多东西,又可以解决困难问题。(第一次写博文,有些罗嗦,希望大牛们能给出指导)