如何排查oom android glide oom

在Android开发中,有哪些好的内存优化方式?如何避免&Out&Of&Memory(OOM)&?
在Android中,一个Process 只能使用16M内存,要是超过了这个限定就会跳出这个异常。
那么,开发中,有哪些措施能避免这个问题呢?不仅仅是Bitmap的处理,其他方面也会导致OOM。有哪些好的内存优化方式呢?
以下有几个网友给出的7 个答案可以参考:
Billy Cui ,对Android总体开发和架构一知半解
对于OOM,其实最重要的是注意不要Memory Leak。而Memory
Leak是会有多个方面会引起的,比如Drawable, RemoteViews, Receiver,
Cursor,InputStream, MediaPlayer等,此外,如果使用JNI也会因为C或C++的代码导致Memory
除了Memory
Leak,大数据量的操作也会导致OOM,比如之前其他回答提到的Bitmap,还有ListAdapter,如果在getView时处理不当,也很容易导致OOM,所以在ListAdapter时应该尽量使用convertView。
最后,可以用android.os.StrictMode以及Eclipse的MAT工具来进行OOM和Memory
Leak的检测。
我的做法是时间换空间,尽量文件化一些占用内存的数据。最典型的就是ListView中的Bitmap,可以参考这个开源组件的实现。/p/androi…
其将Bitmap都本地文件缓存,内存中只保留最近使用的4张图片,在使用中发现还是会偶尔出现OOM,然后我就将其改为完全的读取文件,内存中不保留图片,使用以后都自动回收,由此扩展的数据文件也同样缓存成文件。
除了Bitmap,其他的地方没想到什么能占用这么大内存,网络下载下来的数据文件?需要都留在内存中吗?目前我做的应用,每次请求的数据大小都比较小,每次Http请求下来的数据都把url转换成文件名,然后缓存成文件,在下次Http请求的时候先根据url来预读文件,暂时不用的数据就释放掉。
经常会OOM我觉得就是在Bitmap处理的时候,比如decodeFile,在往界面上加载图片时,不用直接加载原图,可以进行缩放。一张的图片要加载到一个100*100的ImageView上,直接加载进来大多数都会OOM,可以先用inJustDecodeBounds
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds =
拿到这个图片的大小,再算好缩放比例
int&scale = 0;
scale = (int)(options.outHeight / (float)size);
if (scale &= 0) {
scale&= 1;
options.inSampleSize =
options.inJustDecodeBounds =
再进行decode。
总之就是尽量时间换空间,实际这个时间是非常非常短的,用户体验内的。
刘彧 ,1个月android程序开发经验
对于图片要注意recycle吧,分辨率大的图片也要使用BitmapFactory.decode来降低分辨率,减少内存占用,资源图片也要注意不要使用太大的图片
邓桥 ,来自武汉,从事移动互联网相关开发工作,主…
图片销毁一定要recycle,尽量使用SoftReference,网上可以找到资料。
刘沛 ,专业回答技术问题。
使用泛型算法或者泛型容器多多少少都会引起Memory Leak,在现代面向对象编程中似乎Memory
Leak已经无法避免了,总之能自己写的算法就不要用泛型。
李昊 ,足球,电影,手机应用
网络上很多相关内容,但是没有统一的解决方案
最后都会说
1.recycle所有对象,排除程序对该对象的引用,然后System.gc(),但有时候不能根本的解决问题,因为android内部的对内存的限制机制导致的
2.bitmap是在我开发过程中,oom最多的地方,最后结论是,只能大幅度的缩小图片比例
黄杲 ,智能手机设计行业从业者
可以用MAT来检查内存泄露,或者用Monkey Test也能够帮助发现应用的内存泄露。
但内存使用的优化就看工程师的功力了。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。博客访问: 604249
博文数量: 69
博客积分: 6236
博客等级: 准将
技术积分: 606
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Android平台
[转自: http://blog.csdn.net/chengyingzhilian/article/details/8662849]
从早期G1的192MB RAM开始,到现在动辄1G -2G
RAM的设备,为单个App分配的内存从16MB到48MB甚至更多,但OOM从不曾离我们远去。这是因为大部分App中图片内容占据了50%甚至75%
以上,而App内容的极大丰富,所需的图片越来越多,屏幕尺寸也越来越大分辨率也越来越高,所需的图片的大小也跟着往上涨,这在大屏手机和平板上尤其明
显。而且还经常要兼容低版本的设备。所以Android的内存管理显得极为重要。
在这里我们主要讲两件事情:
1.Gingerbread和Honeycomb中的一些影响你使用内存的变化
-heap size
2.理解heap的用途分配
-merory leaks
-Eclispe Memory Analyzer(MAT)
首先第一部分,我们都知道Android是个多任务操作系统,同时运行着很多程序,都需要分配内存,不可能为一个程序分配越来越多的内存以至于让整个系统
都崩溃,因此heap的大小有个硬性的限制,跟设备相关,从发展来说也是越来越大,G1:16MB,Droid:24MB,Nexus
One:32MB,Xoom:48MB,但是一旦超出了这个使用的范围,OOM便产生了。如果你正在开发一个应用,想知道设备的heap大小的限制是多
少,比方说根据这个值来估算自己应用的缓存大小应该限制在什么样一个水平,你可以使用ActivityManager.getMemoryClass
()来获得一个单位为MB的整数值,一般来说最低不少于16MB,对于现在的设备而言这个值会越来越大,24MB,32MB,48MB甚至更大。
但是对于一些内存非常吃紧的比如图片浏览器等应用,在平板上所需的内存更大了。因此在Honeycomb之后AndroidManifest.xml增加了largeHeap的选项
<application android:largeHeap="true" ... </application>
这允许你的应用使用更多的heap,可以用ActivityManager.getLargeMemoryClass ()返回一个更大的可用heap
size。但是这里要警告的是,千万不要因为你的应用报OOM了而使用这个选项,因为更大的heap
size意味着更多的GC时间,意味着应用的性能越来越差,而且用户也会发现其他应用很有可能会内存不足。只有你需要使用很多的内存而且非常了解每一部分
内存的用途,这些所需的内存都是不可或缺的,这个时候你才应该使用这个选项。
刚刚我们提到更大的heap size意味着更多的GC时间,下面我们来谈谈Garbage Collection。
如上图所示,GC会选择一些它了解还存活的对象作为内存遍历的根节点,比方说thread
stack中的变量,JNI中的全局变量,zygote中的对象等,然后开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC
Roots的就是需要回收的垃圾,会被GC回收掉。如下图蓝色部分。
因此也可以看出,更大的heap size需要遍历的对象更多,回收垃圾的时间更长,所以说使用largeHeap选项会导致更多的GC时间。
在Gingerbread之前,GC执行的时候整个应用会暂停下来执行全面的垃圾回收,因此有时候会看到应用卡顿的时间比较长,一般来
说>100ms,对用户而言已经足以察觉出来。Gingerbread及以上的版本,GC做了很大的改进,基本上可以说是并发的执行,也不是执行完
全的回收,只有在GC开始以及结束的时候会有非常短暂的停顿时间,一般来说<5ms,用户也不会察觉到。
在Honeycomb之前,Bitmap的内存分配如下图。
蓝色部分是Dalvik heap,黄色部分是Bitmap引用对象的堆内存,而Bitmap实际的Pixel Data是分配在Native
Memory中。这样做有几个问题,首先需要调用reclyce()来表明Bitmap的Pixel
Data占用的内存可回收,不调用这个方法的话就要靠finalizer来让GC回收这部分内存,但了解finalizer的应该都知道这相当的不可靠;
其次是很难进行Debug,因为一些内存分析工具是查看不到Native Memory的;再次就是不调用reclyce()需要回收Native
Memory中的内存的话会导致一次完整的GC,GC执行的时候会暂停整个应用。
Honeycomb之后,Bitmap的内存分配做出了改变,如下图
蓝色黄色部分没有变化,但Bitmap实际的Pixel Data的内存也同样分配在了Dalvik
heap中。这样做有几个好处。首先能同步的被GC回收掉;其次Debug变得容易了,因为内存分析工具能够查看到这部分的内存;再次就是GC变成并发
了,可做部分的回收,也就是极大缩短了GC执行时暂停的时间。
接下来我们讲第二部分。一般来说我们希望了解我们应用内存分配,最基本的就是查看Log信息。比方说看这样一个Log信息(这是Gingerbread版本的,Honeycomb以后log信息有改动):
D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/
9991K, external K, paused 2ms 2ms
GC_XXX表明是哪类GC以及触发GC的原因。几种GC类型:
- GC_CONCURRENT:这是因为你的heap内存占用开始往上涨了,为了避免heap内存满了而触发执行的。
- GC_FOR_MALLOC:这是由于concurrent gc没有及时执行完而你的应用又需要分配更多的内存,内存要满了,这个时候不得不停下来进行malloc gc。
- GC_EXTERNAL_ALLOC:这是为external分配的内存执行的GC,也就是上文提到的Bitmap Pixel Data之类的。
- GC_HPROF_DUMP_HEAP:这是当你做HPROF这样一个操作去创建一个HPROF profile的时候执行的。
- GC_EXPLICIT:这是由于你显式的调用了System.gc(),这是不提倡的,一般来说我们可以信任系统的GC。
freed 2049K表明在这次GC中回收了多少内存。
65% free K是heap的一些统计数据,表明这次回收后65%的heap可用,存活的对象大小3571K,heap大小是9991K。
external K是Native Memory的数据。放Bitmap Pixel Data或者是NIO Direct
Buffer之类的。第一个数字表明Native
Memory中已分配了多少内存,第二个值有点类似一个浮动的阀值,表明分配内存达到这个值系统就会触发一次GC进行内存回收。
paused 2ms 2ms表明GC暂停的时间。从这里你可以看到越大的heap size你需要暂停的时间越长。如果是concurrent
gc你会看到2个时间一个开始一个结束,这时间是很短的,但如果是其他类型的GC,你很可能只会看到一个时间,而这个时间是相对比较长的。
通过Log可以对内存信息有个基本的了解,但这不足以了解什么对象在使用内存,在哪使用了内存。这时候你需要用Heap Dumps。一个Heap
Dump基本上来说就是一个包含你heap中所有对象信息的二进制文件。你可以用DDMS来生成这个文件,点击DDMS中下图的那个按钮。
同时Heap Dumps也有对应的API,你想要在特定的时间点获取一份Heap
Dump,使用android.os.Debug.dumpHprofData()。获取到的文件要转换成标准的HPROF格式,使用如下命
令:hprof-conv orig.hprof converted.hprof。然后用MAT或者jhat进行分析。
在讲MAT之前先讲下Memory Leaks。要清楚GC并不能防止Memory Leaks,所谓Memory
Leaks就是引用到了已经没用的对象从而让这些对象避免了被GC回收,跟C/C++中的概念并不一样。容易导致内存泄漏的是一些
Activity,Context,View,Drawable之类的引用,和一些非静态的内部类比方说Runnable之类的以及一些Caches。比
如你旋转屏幕的时候在新的方向上产生一个新的Activity,如果有变量引用到旧的Activity就会导致其无法被GC,造成Memory
通常通过上面介绍的Log信息,只要已用memory一直处于上升的情形而不回落,便大致能了解到应用存在Memory
Leaks。不过MAT这类工具可以帮助你更好的对memory进行分析。使用MAT之前有2个概念是要掌握的:Shallow
heap和Retained heap。Shallow heap表示对象本身所占内存大小,一个内存大小100bytes的对象Shallow
heap就是100bytes。Retained
heap表示通过回收这一个对象总共能回收的内存,比方说一个100bytes的对象还直接或者间接地持有了另外3个100bytes的对象引用,回收这
个对象的时候如果另外3个对象没有其他引用也能被回收掉的时候,Retained
heap就是400bytes。
MAT使用Dominator Tree这样一种来自图形理论的概念。
所谓Dominator,就是Flow Graph中从源节点出发到某个节点的的必经节点。那么根据这个概念我们可以从上图左侧的Flow
Graph构造出右侧的Dominator Tree。这样一来很容易就看出每个节点的Retained heap了。Shallow
heap和Retained heap在MAT中是非常有用的概念,用于内存泄漏的分析。
我们用Honeycomb3.0中的HoneycombGallery做一个Demo。在工程的MainActivity当中加入如下代码:
public class MainActivity extends Activity implements ActionBar.TabListener { static Leaky leak = null; class Leaky { void doSomething() { System.out.println("Wheee!!!"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(); } ...
上面这段代码,对Java熟悉的同学都应该了解内部类对象持有了外部类对象引用,而leak作为静态变量在非空判断下只产生了一个对象,因此当旋转屏幕时生成新的Activity的时候旧的Activity的引用依然被持有,如下图:
通过观察旋转屏幕前后Log中GC的信息也能看出heap的分配往上涨了许多,并且在GC执行完heap的分配稳定之后并没有降下来,这就是内存泄漏的迹象。
我们通过MAT来进行分析。先下载,
可以作为Eclipse插件下载,也可以作为RCP应用下载,本质上没有区别。DDMS中选中应用对应的进程名,点击Dump HPROF
file的按钮,等一小段时间生成HPROF文件,如果是Eclipse插件的话,Eclipse会为这个HPROF自动转化成标准的HPROF并自动打
开MAT分析界面。如果是作为RCP应用的话,需要用sdk目录tools中的hprof-conv工具来进行转化,也就是上文提及的命令hprof-
orig.hprof
converted.hprof,这种方式保存HPROF文件的位置选择更为自主,你也可以修改Eclipse的设置让Eclipse提示保存而不是自动
打开,在Preferences -> Android -> DDMS中的HPROF Action由Open in
Eclipse改为Save to disk。打开MAT,选择转化好的HPROF文件,可以看到Overview的界面如下图:
中间的饼状图就是根据我们上文所说的Retained heap的概念得到的内存中一些Retained
Size最大的对象。点击饼状图能看到这些对象类型,但对内存泄漏的分析还远远不够。再看下方Action中有Dominator
Tree和Histogram的选项,这一般来说是最有用的工具。还记得我们上文说过的Dominator
Tree的概念吗,这就是我们用来跟踪内存泄漏的方式。点开Dominator Tree,会看到以Retained
heap排序的一系列对象,如下图:
Resources类型对象由于一般是系统用于加载资源的,所以Retained
heap较大是个比较正常的情况。但我们注意到下面的Bitmap类型对象的Retained
heap也很大,很有可能是由于内存泄漏造成的。所以我们右键点击这行,选择Path To GC Roots ->exclude weak
references,可以看到下图的情形:
Bitmap最终被leak引用到,这应该是一种不正常的现象,内存泄漏很可能就在这里了。MAT不会告诉哪里是内存泄漏,需要你自行分析,由于这是Demo,是我们特意造成的内存泄漏,因此比较容易就能看出来,真实的应用场景可能需要你仔细的进行分析。
根据我们上文介绍的Dominator的概念,leak对象是该Bitmap对象的Dominator,应该出现在Dominator
Tree视图里面,但实际上却没有。这是由于MAT并没有对weak references做区别对待,这也是我们选择exclude weak
references的原因。如果我们Path To GC Roots ->with all references,我们可以看到下图的情形:
可以看到还有另外一个对象在引用着这个Bitmap对象,了解weak references的同学应该知道GC是如何处理weak references,因此在内存泄漏分析的时候我们可以把weak references排除掉。
有些同学可能希望根据某种类型的对象个数来分析内存泄漏。我们在Overview视图中选择Actions -> Histogram,可以看到类似下图的情形:
上图展示了内存中各种类型的对象个数和Shallow heap,我们看到byte[]占用Shallow
heap最多,那是因为Honeycomb之后Bitmap Pixel Data的内存分配在Dalvik
heap中。右键选中byte[]数组,选择List Objects -> with incoming
references,可以看到byte[]具体的对象列表:
我们发现第二个byte[]的Retained heap较大,内存泄漏的可能性较大,因此右键选中这行,Path To GC Roots ->
exclude weak references,同样可以看到上文所提到的情况,我们的Bitmap对象被leak所引用到,这里存在着内存泄漏。
在Histogram视图中第一行中输入com.example.android.hcgallery,过滤出我们自己应用中的类型,如下图:
我们发现本应该只有一个MainActivity现在却有两个,显然不正常。右键选择List Objects -> with incoming
references,可以看到这两个具体的MainActivity对象。右键选中Retained
heap较大的MainActivity,Path To GC Roots -> exclude weak
references,再一次可疑对象又指向了leak对象。
以上是MAT一些基本的用法,如果你感兴趣,可以自行深入的去了解MAT的其他功能。
阅读(502) | 评论(0) | 转发(0) |
下一篇:没有了
相关热门文章
给主人留下些什么吧!~~
请登录后评论。下次自动登录
关注移动互联网和移动APP开发工具、开发框架、测试工具、微信开发、Android源码、Android开源类库以及各种开源组件的IT科技网站
现在的位置:
关于android 使用bitmap的OOM心得和解决方案
android开发,从2010年开始学习到现在的独立完成一个app,这漫长的四年,已经经历了很多次bug的折磨,无数次的加班训练。然而,自以为自己已经比较了解android了,却最近在一个项目上,因为oom而折腾了一个周,回到原地,认识了自己的不足,感觉自己是如此的菜鸟呀。
好了,不废话,大家在使用开发android的时候,很少会注意或者意识到释放内存的重要性,因为大家在使用过程中,涉及的图片资源不多,或者比较稳定,来回切换界面,图片也就那么几张或者使用的都是很小的图片,根本不会感觉到图片占用内存可能引发的潜在危机。
如果你的程序中,使用了一下功能,你作为一个合&#26684;的android开发工程师,你有必要,注意oom的潜在危机
1.界面比较多,并且很多界面的背景图片不一样;
2.涉及到换肤功能,定义多种皮肤,皮肤的资源不是使用color 而是图片资源;
以上两种情况,如果你不注意合理的释放内存,你将会为自己程序莫名其妙的崩溃付出代价的。
我们在android程序中,无论是使用layout布局设置了背景还是使用了setBackgroundResource 设置背景,你都以为不需要释放内存?这绝对是一个错误的观念,绝对的错误。也许这一点的错误的认识,将会在大屏幕手机上,暴露出来你的oom的现象,特别是在三星的大屏幕手机,爆oom的几率更大。
也许,大家在使用开发中,会注意这一点,使用了一下方式去解决释放内存(网上很多例子都是使用该方式去释放内存),比如:
View view = findViewById(R.id.page_bg);
BitmapDrawable bitmapDrawable = (BitmapDrawable) view.getBackground();
view.setBackgroundResource(0);
bitmapDrawable.setCallback(null);
Bitmap bitmap = bitmapDrawable.getBitmap();
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
System.gc();
从我们的测试效果来看看logcat 打印出来的结果
05-07 06:55:39.330: D/dalvikvm(6988): GC_FOR_ALLOC freed 4091K, 31% free K, paused 44ms, total 45ms
05-07 06:55:39.340: I/dalvikvm-heap(6988): Grow heap (frag case) to 13.126MB for 3686416-byte allocation
05-07 06:55:39.421: D/dalvikvm(6988): GC_CONCURRENT freed <1K, 5% free 1K, paused 6ms+5ms, total 83ms
05-07 06:55:39.600: D/dalvikvm(6988): GC_FOR_ALLOC freed <1K, 5% free 1K, paused 44ms, total 44ms
05-07 06:55:39.670: I/dalvikvm-heap(6988): Grow heap (frag case) to 19.377MB for 6554896-byte allocation
05-07 06:55:39.790: D/dalvikvm(6988): GC_CONCURRENT freed 0K, 4% free 1K, paused 7ms+20ms, total 114ms
05-07 06:55:40.011: I/System.out(6988): onCreate
05-07 06:55:41.760: D/dalvikvm(6988): GC_EXPLICIT freed 10759K, 56% free K, paused 4ms+7ms, total 111ms
05-07 06:55:41.821: D/dalvikvm(6988): GC_EXPLICIT freed <1K, 56% free K, paused 4ms+7ms, total 62ms
05-07 06:55:41.821: I/System.out(6988): onDestroy
从以上logcat中,我们看到了GC_EXPLTCIT 为我们释放了10759k的内存,这个是很可观的,也许这一刻你会沾沾自喜,会认为你已经解决了程序中oom的潜在危机。
然而,实际上,你却引发了另外一个潜在的问题,如果有A和B界面,同时使用了一个背景,你在A中释放了,在B中去使用,就会导致了一下error的logcat
05-07 07:00:37.250: D/dalvikvm(6988): GC_EXPLICIT freed 6411K, 81% free K, paused 4ms+6ms, total 105ms
05-07 07:00:37.310: D/dalvikvm(6988): GC_EXPLICIT freed 88K, 82% free K, paused 3ms+8ms, total 59ms
05-07 07:00:37.310: I/System.out(6988): onDestroy
05-07 07:00:37.891: D/AndroidRuntime(6988): Shutting down VM
05-07 07:00:37.891: W/dalvikvm(6988): threadid=1: thread exiting with uncaught exception (group=0x40a71930)
05-07 07:00:37.991: E/AndroidRuntime(6988): FATAL EXCEPTION: main
05-07 07:00:37.991: E/AndroidRuntime(6988): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.graphics.Canvas.throwIfRecycled(Canvas.java:1026)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.graphics.Canvas.drawBitmap(Canvas.java:1127)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:393)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.View.draw(View.java:13697)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.View.draw(View.java:13596)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.View.draw(View.java:13594)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.View.draw(View.java:13594)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.View.draw(View.java:13715)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.widget.FrameLayout.draw(FrameLayout.java:467)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2211)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2281)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2177)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2045)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1854)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.Choreographer.doCallbacks(Choreographer.java:562)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.Choreographer.doFrame(Choreographer.java:532)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.os.Handler.handleCallback(Handler.java:725)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.os.Handler.dispatchMessage(Handler.java:92)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.os.Looper.loop(Looper.java:137)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at android.app.ActivityThread.main(ActivityThread.java:5041)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at java.lang.reflect.Method.invokeNative(Native Method)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at java.lang.reflect.Method.invoke(Method.java:511)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-07 07:00:37.991: E/AndroidRuntime(6988):
at dalvik.system.NativeStart.main(Native Method)
感觉很奇怪把,因为你在这句logcat中,根本找不到错误的地方和为什么发生这样的错误,但是这个错误却是致命的。从英文的字面意思理解:你使用了一个已经释放的Bitmap来绘制你的界面。
遇到这样的情况,也许40%的android程序员都不知所错,因为我们根据无法把握出错的地方,也许你认为你是按照Activity的生命周期来做填充背景和释放背景,却出现这样的错误。
网上的资料解释,你能理解,却还是无法找到具体的问题所在。
我经历过这个问题的痛苦,看了网上的解释和解决办法,根本就找不到靠谱的。我对待这个问题,始终不放弃,经过了多次的测试和验证,该问题出现的地点是有一下两种可能性:
情况1、 你A界面中使用了a.png图片,然后做了跳转,finish了A界面,finish的时候,你使用了一下的代码释放了内存:
View view = findViewById(R.id.page_bg);
BitmapDrawable bitmapDrawable = (BitmapDrawable) view.getBackground();
view.setBackgroundResource(0);
bitmapDrawable.setCallback(null);
Bitmap bitmap = bitmapDrawable.getBitmap();
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
System.gc();
然后再次进入A界面,极有可能出现该问题。
情况2、 你在A界面使用了a.png图片,然后在B界面也使用了a.png, A界面没有finish,B界面使用了finsh,并且使用上面同样的方式释放了内存。然后我们正常情况下,A界面会经历onResume的方式来显示,可是这个时候,我们的a.png在内存中已经释放了,所以就会引发上面的
05-07 07:00:37.991: E/AndroidRuntime(6988): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
看着字面很简单,很容易理解,实际中找到问题的所在却不是容易的一件事。
也许,你在这一刻是否已经抓狂了,难道就没办法来释放内存来解决潜在的oom问题?
答案是否。像google这样的大公司,他们提供的api,考虑的潜在问题比我们正常程序员考虑的还要深入N倍。
个人从新了相关的api,得出了一下解决办法来释放内存,解除我们潜在的oom情况
无论你是在xml中布局使用了:
android:background
还是在java代码中调用了:
setBackground(background);
setBackgroundDrawable(background)
setBackgroundResource(resid)
的方式去设置了背景图片.
使用的时候,请调用一下对应的方法:
setBackgroundResource和android:background →setBackgroundResource(0);
setBackgroundDrawable(background)→
setBackgroundDrawable(null)
setBackground(background)
→ setBackground(null)
然后再onDestory中调用System.gc();
以上的代码,我们修改为:
View view = findViewById(R.id.page_bg);
view.setBackgroundResource(0);
System.gc();
我们在看看我们的logcat的结果:
05-07 07:29:18.941: D/dalvikvm(7598): GC_FOR_ALLOC freed 27K, 65% free K, paused 43ms, total 58ms
05-07 07:29:18.970: I/dalvikvm-heap(7598): Grow heap (frag case) to 13.203MB for 3686416-byte allocation
05-07 07:29:19.041: D/dalvikvm(7598): GC_FOR_ALLOC freed 83K, 52% free 1K, paused 72ms, total 72ms
05-07 07:29:19.140: D/dalvikvm(7598): GC_CONCURRENT freed <1K, 52% free 1K, paused 4ms+29ms, total 105ms
05-07 07:29:19.240: D/dalvikvm(7598): GC_FOR_ALLOC freed <1K, 52% free 1K, paused 40ms, total 40ms
05-07 07:29:19.310: I/dalvikvm-heap(7598): Grow heap (frag case) to 19.373MB for 6554896-byte allocation
05-07 07:29:19.420: D/dalvikvm(7598): GC_CONCURRENT freed 0K, 29% free 1K, paused 4ms+31ms, total 106ms
05-07 07:29:19.590: I/System.out(7598): onCreate
05-07 07:29:21.290: D/dalvikvm(7598): GC_EXPLICIT freed 10749K, 56% free K, paused 3ms+8ms, total 125ms
05-07 07:29:21.290: I/System.out(7598): onDestroy
以上的界面和我们调用了手动释放的方式是不是一样? 呵呵,反复测试多次,看看还会不会导致java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
实际上,我们的程序不会再出现上面的问题了,并且解决了oom得潜在危机。如此的简单,如此的便捷。
同理:如果我们在程序中,使用了ImageView的话, 我们也可以使用
imageView.setImageResource(0);
这种方式,来释放我们设置的android:src或者bitmap等等。
那么,关于图片的oom可能潜在的问题,我们告一段落,从这次的经历,让我更加的深入的了解java语言的内存回收机制,总结一句话:尽可能的让去释放内存,我们只负责标示需要释放的内存。通俗点:我们只是标记需要杀的人,谁来杀,就让杀手看着办。
作者:SpringSky
出处:http://blog.csdn.net/springsky_/article/details/
blog:http://blog.csdn.net/springsky_
本文版权归作者和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【上篇】【下篇】

我要回帖

更多关于 android oom adj 的文章

 

随机推荐