Android内存溢出分析

来源:互联网 发布:淘宝宝贝卖点范文 编辑:程序博客网 时间:2024/05/03 08:39

http://blog.tisa7.com/tech/android_memory_management_and_solve_oom_problem.html


内存溢出,是Android开发中常遇到的问题,解决起来总是摸不着头脑。今天爬爬就来讲讲如何定位内存溢出。

1. OOM(内存溢出)和Memory Leak(内存泄露)有什么关系?

OOM可能是因为Memory Leak,也可能是你的应用本身就比较耗内存(比如图片浏览型的,或者应用本身的设计有问题)。所以,出现OOM不一定是Memory Leak。

同样,Memory Leak也不一定就会导致OOM,如果泄露的速度很慢,可能还没用完可用内存应用就被重启了,那就不会OOM咯。当然了,有bug解决了最好。

2. 什么是shallow heap与retained heap?

  • shallow heap:你自身占了多少内存,比如你有一个int属性,就占4字节。不包括你引用的其他对象。
  • retained heap:如果你被销毁,总共会释放多少内存。这些因你存在被占据的空间就是retained heap。
  • 更详细的解释请看这篇博客

3. 什么是GC roots?

GC的时候,是从这些节点开始遍历,不停的寻找其子节点直到结束。然后把不能遍历到的节点释放。这些遍历的起点(注意,可不是一个哦)就叫做GC roots。

那,对于java来说,谁是GC roots?简单点说(不是那么准确)包括以下几种:

  • 栈上面的局部变量
  • 栈上面的函数参数变量
  • 所有由Bootstrap Loader加载的类变量
  • 另外,JNI相关的也会有
  • 更多详细解释请看这篇博客

其实到最后,谁是GC roots不是那么重要,因为一般来说,到最后就剩下一些系统框架类,以及jvm和class相关的东西。这里给大家说GC roots主要是因为使用mat需要了解它。

4. 怎样使用MAT定位内存泄露?

4.1 看Histogram(类统计图)

histogram视图显示了每个类有多少实例,并可以按照这些实例占据的Retained size和Shallow size排序。通过过滤包名,很容易发现有问题的类。

这里有几个简单的原则,比如,activity的实例通常只应该有一个。已经关闭的activity不应该出现。实体类的Retained size应该是比较小的,也就几十KB。

histogram未过滤

对于Android程序来说,内存泄露通常都会牵扯到activity。因此,dump之前,可以多旋转几次屏幕并反复的进出可能有问题的activity,让问题尽可能的凸现。
通过Histogram我们可以看每个类有多少个实例,shallow和retained heap分别有多大。如果只是看java的基础类型和framework的类,没有什么意义,一定要过滤出自己的类型,如下图

Histogram 已过滤cn

发现LeakInnerClassActivity产生了9个实例,一定是被hold住了。

4.2 看Dominator Tree

dominator tree

大家来看这个图,左侧是对象引用关系,右侧是dominator tree

  • Note that A, B and C are dominated by a “virtual” root object.
  • Note that the dominator relationship is transitive;C dominates E which dominates G therefore C also dominates G.

这个视图非常强大,它把所有实例按Retained heap和Shallow heap列出来;并且,只要展开就可以看到这个实例所占有的实例(换句话说,如果该对象被释放,还会有哪些对象被释放)

Dominator Tree

使用这个视图,可以很方便的追踪被泄露的内存到底是谁占用了,更多参考这篇博客

4.3 对比heap dumps,可以更快的定位内存泄露的位置。操作步骤:

  • 打开一个HPROF文件,切换到histogram视图
  • 在Navigation View中右键点击histogram,选择Add to compare basket
  • 打开另一个HPROF文件,并重复上一个步骤
  • 对比两次heap dumps的内容,看下图,LeakInnerClassActivity的实例又增加了一个。而我仅仅是又启动了一次该Activity,所以问题显而易见。

histogram compare

参考:Memory Analysis for Android Applications

5. 内部类怎样使用才会产生内存泄露,以及由此衍生的AsyncTask、Handler问题如何解决?

  • 如果非静态内部类的方法中,有生命周期大于其所在类的,那就有问题了。比如:AsyncTask、Handler,这两个类都是方便开发者执行异步任务的,但是,这两个都跳出了Activity/Fragment的生命周期。(或许,是时候学习Loader了)
  • 为什么?因为非静态内部类会自动持有一个所属类的实例,如果所属类的实例已经结束生命周期,但内部类的方法仍在执行,就会hold其主体。也就使主体不能被释放,亦即内存泄露。
  • 静态类呢?静态类编译后和非内部类是一样的,有自己独立的类名。不会悄悄引用所属类的实例,所以就不容易泄露。
//首先,静态类static class IncomingHandler extends Handler {    //其次,弱引用    private final WeakReference<UDPListenerService> mService;     IncomingHandler(UDPListenerService service) {        mService = new WeakReference<UDPListenerService>(service);    }    @Override    public void handleMessage(Message msg) {         UDPListenerService service = mService.get();         if (service != null) {              service.handleMessage(msg);         }    }}

6. 图片导致的OOM如何解决?

  • 加载时使用option,用多大,载入多大。
  • res目录下的图片也是一样,及时清理过大的图片资源。
  • 如果还有问题,就想办法把不可见的资源释放掉,比如,TabActivity中不可见的Tab,ViewPager中的Fragment。
  • 如果activity的图片资源较多,需要考虑屏幕旋转时,销毁已有资源。请参考这篇文章

7. 需要context的时候用activity还是application?

  • 看使用的周期是否在activity周期内,如果超出,必须用application;常见的情景包括:AsyncTask,Thread,第三方库初始化等等。
  • 还有些情景,只能用activity:比如,对话框,各种View,需要startActivity的等。
  • 总之,尽可能使用Application。参考stackoverflow

8. 什么时候需要手动将变量设置为NULL?

  • 类变量,一旦用完,尽快释放。因为类的存活时间最长(非常长),所以,占用的资源越少越好;
  • 比较耗时且耗内存的方法内的局部变量,比如,图片处理的方法,每个bitmap对象用完就及时丢弃。尽可能早的让gc介入。

参考:http://kohlerm.blogspot.com/


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 4个月婴儿易惊吓怎么办 心里有问题的人怎么办 减肥的时候想吃东西怎么办 大联盟ping很高怎么办 酷派手机弹广告怎么办 孕早期肚子紧绷怎么办 怀孕2个月同房了怎么办 怀孕前三月同房了怎么办 人流前三天同房了怎么办 人流后三天同房了怎么办 吃避孕药后月经量少怎么办 排卵日同房不想怀孕怎么办 排卵日同房没有怀孕怎么办 想怀孕想生儿子怎么办 到期大姨妈不来怎么办 怕怀孕月经不来怎么办 宫颈环扎后宫缩怎么办 来例假刺激外阴高潮了怎么办 客人消费完说没钱怎么办? 孕13周血糖偏高怎么办 餐后血糖高14点怎么办 餐后血糖20多怎么办 空腹血糖6.6饭后9.2怎么办 键盘shift键失效了怎么办 灵异附身夫妻不合怎么办 鞋子买大了半码怎么办 鞋子买小了半码怎么办 黑布鞋鞋面白了怎么办 黑布鞋不黑了怎么办 黑布鞋退白了怎么办 老北京布鞋款式太少怎么办 老人输液抽搐后昏迷怎么办 头七家里有狗怎么办 股票退市股民的钱怎么办 美国股票退市股民怎么办 百度云字幕和视频不同步怎么办 百度云加载字幕有延迟怎么办 很难适应新环境怎么办 蜘蛛丝碰到嘴唇上起包有毒怎么办? 电瓶车在路上爆胎了怎么办 嘴被虫子咬肿了怎么办