在Android中解决内存溢出 – OutOfMemoryError
原文链接: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
将设备或模拟器与Android Studio连接。
在Android Studio中运行程序。
在 屏幕下方点击Android标签,再点击Memory标签,选择设备与要监控的应用。一旦Memory Monitor开始监控设备,就会出现蓝色的图表显示内存使用情况。深蓝色部分为当前应用正在使用的内存,浅蓝色(深蓝色上面那一块)为可以使用、未被分 配的内存空间。

点击垃圾车图标(上图右侧那个小图标)可以触发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进行内存泄露的检测。
打开Android Studio,点击下方的Android Monitor标签。
运行应用,并选择应用来监控。
反复进行某个可能存在内存泄露的操作,比如返回打开关闭某个Activity。
在抛出OutOfMemoryException之前点击Android Monitor中的Memory标签。此时图表开始绘制,过一会点击GC图标。(刚才说的那个红色垃圾车图标)

点击垃圾车下面那个有绿色箭头的图标”Dump Java Heap”,并等待几秒。然后会生成一个.hprof文件,这个文件就是用来分析内存使用的。
下载MAT。
使用sdk中platform-tools路径下的hprof-conv文件将刚才生成的.hprof文件转码成MAT可以解析的文件。
./hprof-conv path/file.hprof exitPath/heap-converted.hprof
使用MAT打开转码后的文件。选择”Leak Suspects Report”然后点击”Finish”。

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

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

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

而后持有引用的的线程就会显示出来。而后你就可以深入观察什么持有了Activity的引用。
通过下面的信息我们可以看到有一个DisplayListener被注册了但从未解除。

所以这个漏洞就找到了,只需要解除监听器就可以了。
不是所有的内存泄露漏洞都这么容易找到。希望这篇文章可以帮你避免内存泄露并在内存泄露发生时找到漏洞。
推荐:
Android 多种方式正确的加载图像,有效避免oom
在Android中解决内存溢出 – OutOfMemoryError的更多相关文章
- 查找并修复Android中的内存泄露—OutOfMemoryError
[编者按]本文作者为来自南非约翰内斯堡的女程序员 Rebecca Franks,Rebecca 热衷于安卓开发,拥有4年安卓应用开发经验.有点完美主义者,喜爱美食. 本文系国内ITOM管理平台 One ...
- android解决内存溢出的问题(没有从根本上解决)
Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完 ...
- linux中tomcat内存溢出解决办法
用命令 tail -f /root/apache-tomcat-6.0.20/logs/catalina.out(需要找到tomcat路径) 查看日志,查看是否有错误 linux中tomcat内存溢出 ...
- 系统剖析Android中的内存泄漏
[转发]作为Android开发人员,我们或多或少都听说过内存泄漏.那么何为内存泄漏,Android中的内存泄漏又是什么样子的呢,本文将简单概括的进行一些总结. 关于内存泄露的定义,我可以理解成这样 没 ...
- linux中tomcat内存溢出
刚开始测试服务器与线上后台都不能上传10分钟以上的视频,后来只要是视频就不能上传,进入服务器查日志得到如下错误: Caused by: java.lang.OutOfMemoryError: Java ...
- Android中的内存管理机制以及正确的使用方式
概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...
- Android中解决图像解码导致的OOM问题
Android中解决图像解码导致的OOM问题 原文链接:http://blog.csdn.net/zjl5211314/article/details/7042017
- android createbitmap函数内存溢出,求解怎样进行处理out of memory溢出问题
android createbitmap函数内存溢出,求解怎样进行处理out of memory溢出问题 android createbitmap函数内存溢出,求解怎样进行处理out of memor ...
- Java架构师中的内存溢出和内存泄露是什么?实际操作案例!
JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让我们来看一看. 01 内存泄漏 & 内存溢出 1.内存泄漏(memory leak ) 申请了内存用完了不释放,比如一共有 102 ...
随机推荐
- JavaWeb笔记(十)非关系型数据库Redis
Redis Redis是一款高性能的NOSQL系列的非关系型数据库 主流的NOSQL产品 键值(Key-Value)存储数据库 相关产品: Tokyo Cabinet/Tyrant.Redis.Vol ...
- 【bzoj4974】字符串大师 逆模拟KMP
题目描述 一个串T是S的循环节,当且仅当存在正整数k,使得S是$T^k$(即T重复k次)的前缀,比如abcd是abcdabcdab的循环节.给定一个长度为n的仅由小写字符构成的字符串S,请对于每个k( ...
- 【Luogu】P2154虔诚的墓主人(树状数组)
题目链接 这题就是考虑我们有这样一个情况
- [bzoj4712] 洪水 [树链剖分+线段树+dp]
题面 传送门 思路 DP方程 首先,这题如果没有修改操作就是sb题,dp方程如下 $dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子 我们令$g[u]=max(dp[v ...
- MySQL基本管理和应用
# yum install mysql-server # /etc/init.d/mysqld startInitializing MySQL database: Installing MySQL s ...
- 转:ListView中getView的工作原理
ListView中getView的工作原理: [1]ListView asks adapter “give me a view” (getView) for each item of the list ...
- HDU 4649 Professor Tian (概率DP)
Professor Tian Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)To ...
- gdb 脚本
不满足于一条一条执行命令,我们可以将命令写进脚本里面,连续执行, 1: gdb启动会在当前目录寻找.gdbinit文件,并读取里面的命令列表 2: 我们可以启动gdb 时,加-x cmd.gdb 来指 ...
- Secret Cow code(USACO)
题目描述:zxyer为了防止他的标程被别人抄走,他在计算机中的rar文件上设置了一个密码,其中每一个密码都有一个初始字符串,字符串由大写字母组成,且长度不超过30位,每一个密码还有一个询问,询问为一个 ...
- 俄罗斯方块(NOIP模拟赛)(水·模拟)
真是一道神奇的题目233~ 原题传送门 迫不得已贴了个题解的链接.. 好吧,这道题就是分情况讨论,纯模拟,, 没有什么难的.. 脑洞要大,四面都要考虑,不能漏! #include<iostrea ...