原文链接:http://riggaroo.co.za/fixing-memory-leaks-in-android-outofmemoryerror/

注:本文在原文基础上在如何判断内存是否泄露方面进行了补充

安卓开发中经常出现内存溢出的情况,没有防备的开发者可能一天会不经意间写好几个内存溢出的漏洞。你可能不会发现这些漏洞,甚至都不知道它们存在,直到你看到这种异常:

java.lang.OutOfMemoryError: Failed to allocate a 4308492 byte allocation with 467872 free bytes and 456KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:2580)
at android.content.res.Resources.loadDrawable(Resources.java:2487)
at android.content.res.Resources.getDrawable(Resources.java:814)
at android.content.res.Resources.getDrawable(Resources.java:767)
at com.nostra13.universalimageloader.core.DisplayImageOptions.getImageOnLoading(DisplayImageOptions.java:134)

这是啥意思呢?难道我的Bitmap太大了?

这种异常信息可能会让你产生误解。当你看到OutOfMemoryError的时候,十有八九是内存泄露。我第一次看到这个就以为我的Bitmap太大了,唉当时的我太年轻了。

什么是内存泄露?

内存泄露指程序中没有正确的释放用完的内存空间,导致运行异常或错误

Android中内存泄露是如何发生的?

事实上Android中内存泄露很容易发生。最大的问题就是Context对象。

每 一个App都有一个全局的Application Context对象,可以通过getApplicationContext()方法获得。每一个Activity都是Context的子类,会存储有关当 前Activity的信息。很多情况下,内存泄露与Activity泄露有关。比如有的开发者会将Context对象在各个线程里传来传去,写一些静态的 TextView,让它们持有Activity的引用,这就是一个典型的错误。

判断是否有内存泄露

Memory Monitor

  1. 将设备或模拟器与Android Studio连接。

  2. 在Android Studio中运行程序。

  3. 在 屏幕下方点击Android标签,再点击Memory标签,选择设备与要监控的应用。一旦Memory Monitor开始监控设备,就会出现蓝色的图表显示内存使用情况。深蓝色部分为当前应用正在使用的内存,浅蓝色(深蓝色上面那一块)为可以使用、未被分 配的内存空间。

  4. 点击垃圾车图标(上图右侧那个小图标)可以触发GC(内存回收)。

有内存溢出漏洞的应用在不断操作并触发GC后的图表不断上升的:

内存泄露被解决后的应用不断操作并不断触发GC后图表波动但维持稳定:

DDMS

打 开DDMS界面,在左侧面板中选择要观察的进程,点击左上角的Update Heap按钮(红圈圈出的圆柱体按钮),再点击右侧面板中的Heap标签,然后不断操作并持续点击Cause GC按钮,观察表中data object的Total Size的数值变化。如果持续增高而不下降,那就很可能是有内存泄露发生了。

有内存泄露时,应用不会重新得到用过的内存空间,当用到300MB的时候,OutOfMemoryError就发生了。从修复了内存泄露的应用的图表可以看出,应用可以回收一些内存,并处在长期稳定状态。

如何避免内存泄露?

  • 不要将Context对象传给activity与fragment以外的对象。

  • 永远不要将Context和View存储在静态变量中。

      private static TextView textView;//不要这么干
    private static Context context;//不要这么干
  • 在onPause()/onDestroy()方法中解除监听器,包括在Android自己的Listener,Location Service或Display Manager Service以及自己写的Listener。

  • 不要在后台线程与AsyncTask中存储activity的强引用。不然当Activity被关闭后,由于AsyncTask仍在执行且持有Activity的强引用,导致Activity无法被回收。

  • 使用Application Context而不是Activity的Context

  • 尽量不要用非静态内部类,因为它会持有外部类的引用。在非静态内部类中存储Activity或View的引用会导致内存泄露。如需存储就使用WeakReference。

如何解决内存泄露?

解决内存泄露需要大量的错误积累与练习。内存泄露可能会很难追踪,但借助一些工具可以帮助你识别可能的漏洞。这里我们使用MAT进行内存泄露的检测。

  1. 打开Android Studio,点击下方的Android Monitor标签。

  2. 运行应用,并选择应用来监控。

  3. 反复进行某个可能存在内存泄露的操作,比如返回打开关闭某个Activity。

  4. 在抛出OutOfMemoryException之前点击Android Monitor中的Memory标签。此时图表开始绘制,过一会点击GC图标。(刚才说的那个红色垃圾车图标)

  5. 点击垃圾车下面那个有绿色箭头的图标”Dump Java Heap”,并等待几秒。然后会生成一个.hprof文件,这个文件就是用来分析内存使用的。

  6. 下载MAT

  7. 使用sdk中platform-tools路径下的hprof-conv文件将刚才生成的.hprof文件转码成MAT可以解析的文件。

     ./hprof-conv path/file.hprof exitPath/heap-converted.hprof
  8. 使用MAT打开转码后的文件。选择”Leak Suspects Report”然后点击”Finish”。

  9. 点击界面上方三条竖杠的图标。而后你会看到占用内存的对象的列表。

  10. 这个列表可能难以理解,你可以输入类名来过滤结果。我建议在输入框中输入package name,如下:

  11. 现 在我们可以看到VideoDetailActivity的9个实例,这显然不对,因为我们只应该有一个。我们可以右键单击这一条目并选择”Merge Paths to Shortest GC Root”并点击“exclude all phantom/weak/soft etc.references”来看是什么持有VideoDetailActivity的引用。

    而后持有引用的的线程就会显示出来。而后你就可以深入观察什么持有了Activity的引用。

  12. 通过下面的信息我们可以看到有一个DisplayListener被注册了但从未解除。

所以这个漏洞就找到了,只需要解除监听器就可以了。

不是所有的内存泄露漏洞都这么容易找到。希望这篇文章可以帮你避免内存泄露并在内存泄露发生时找到漏洞。

推荐:

Android 多种方式正确的加载图像,有效避免oom

在Android中解决内存溢出 – OutOfMemoryError的更多相关文章

  1. 查找并修复Android中的内存泄露—OutOfMemoryError

    [编者按]本文作者为来自南非约翰内斯堡的女程序员 Rebecca Franks,Rebecca 热衷于安卓开发,拥有4年安卓应用开发经验.有点完美主义者,喜爱美食. 本文系国内ITOM管理平台 One ...

  2. android解决内存溢出的问题(没有从根本上解决)

    Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完 ...

  3. linux中tomcat内存溢出解决办法

    用命令 tail -f /root/apache-tomcat-6.0.20/logs/catalina.out(需要找到tomcat路径) 查看日志,查看是否有错误 linux中tomcat内存溢出 ...

  4. 系统剖析Android中的内存泄漏

    [转发]作为Android开发人员,我们或多或少都听说过内存泄漏.那么何为内存泄漏,Android中的内存泄漏又是什么样子的呢,本文将简单概括的进行一些总结. 关于内存泄露的定义,我可以理解成这样 没 ...

  5. linux中tomcat内存溢出

    刚开始测试服务器与线上后台都不能上传10分钟以上的视频,后来只要是视频就不能上传,进入服务器查日志得到如下错误: Caused by: java.lang.OutOfMemoryError: Java ...

  6. Android中的内存管理机制以及正确的使用方式

    概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...

  7. Android中解决图像解码导致的OOM问题

    Android中解决图像解码导致的OOM问题 原文链接:http://blog.csdn.net/zjl5211314/article/details/7042017

  8. android createbitmap函数内存溢出,求解怎样进行处理out of memory溢出问题

    android createbitmap函数内存溢出,求解怎样进行处理out of memory溢出问题 android createbitmap函数内存溢出,求解怎样进行处理out of memor ...

  9. Java架构师中的内存溢出和内存泄露是什么?实际操作案例!

    JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让我们来看一看. 01 内存泄漏 & 内存溢出 1.内存泄漏(memory leak ) 申请了内存用完了不释放,比如一共有 102 ...

随机推荐

  1. python中的re模块,常用函数介绍

    参考: http://www.cnblogs.com/tina-python/p/5508402.htm ======== 1,预定义字符集,可以写在字符集[....]中 \d  数字: \D 非数字 ...

  2. [ARC068F] Solitaire [DP]

    题面 传送门 思路 单调性 首先,显然可以发现这些数在放进双端队列之后肯定是一个$V$形的排布:1在最中间,两边的数都是单调递增 那么我们拿出来的数,显然也可以划分成2个单调递减的子序列(因为我们也是 ...

  3. Installing Wine 1.5: configure: error: Cannot build a 32-bit program, you need to install 32-bit development libraries(转载)

    Installing Wine 1.5: configure: error: Cannot build a 32-bit program, you need to install 32-bit dev ...

  4. MFC 在窗口上画指定大小的ICON

    CPaintDC dc(this); HICON hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON) ...

  5. Linux命令之time

    我使用过的Linux命令之time - 测定一个命令的资源使用情况 本文链接:http://codingstandards.iteye.com/blog/798788   (转载请注明出处) 用途说明 ...

  6. Linux下kill命令

    首先了解什么是信号:信号是进程级的中断请求,系统定义了30余种信号,kill是管理员用来发送信号的一种手段. 功能说明:删除执行中的程序或工作. 语 法:kill [-s <信息名称或编号> ...

  7. 关于tbody js取法兼容。

    本来jquery就是为了兼容才用的,动态添加表格的时候,ie必须要append 到tbody上,不然无法取上. 而chrome等则可以,最终还是选appendto tobody

  8. redis集群PHP解决方案

    Redis3.2.4 Cluster集群搭建 服务器环境:192.168.3.229192.168.3.193每台服务器搭建3个节点,组成3个主节点,3个从节点的redis集群. 注意:防火墙一定要开 ...

  9. input框监控输入内容

    $(".input").bind("input porpertychange",function(){ console.log($(".input&q ...

  10. error while loading shared libraries:libmysqlclient.so.18 错误

    error while loading shared libraries:libmysqlclient.so.18错误 新手安装php的时候如果出现这种问题,解决办法很简单,就是查看你的mysql安装 ...