Android没有释放Bitmap内存

4
我已经采取了以下措施来处理应用程序中的位图:
  1. 使用占用内存1/8大小的LruCache存储Bitmaps
  2. 使用BitmapFactory.Options计算inSampleSize
  3. 在创建Bitmaps时捕获OOM,调用evictAllSystem.gc()
  4. 有时候还会使用AsyncTaskBitmaps进行解码
我使用BitmapFactory.decodeFile,但是看起来虚拟机无法快速释放内存中的Bitmaps。我在某个地方读到说可能存在使用BitmapFactory.decodeFile的错误,因此我尝试使用BitmapFactory.decodeFileDescriptor,但是我随机收到以下内容:

skia --- decoder->decode returned false

因此,如果我想使用BitmapFactory.decodeFileDescriptor,是否需要修复FileInputStream或其他问题?
这花费了我太多时间,我已经阅读了所有基于此的解决方案以及Google关于Bitmap处理的建议,但我现在陷入了死局。
谢谢。

你想对“位图”做什么?如果你问这个问题,我相信你经常加载它们。 - Maxim Shoustin
1
我怀疑你在ListView或GridView中使用了位图,导致了OutOfMemoryError错误。 如果是这种情况,你应该考虑使用Bitmap的WeakReferences。 你能否进一步阐述你的情况? - Pankaj
在ListView和GridView中,也可以进行全屏缩放。 - Niko
请查看此链接 https://github.com/nostra13/Android-Universal-Image-Loader 我认为这可能会对您有所帮助。 - android_dev
如果我在LruCache中使用Bitmap的WeakReferences,当条目被驱逐且Bitmap没有被使用时,垃圾回收器是否会更好地工作? - Niko
显示剩余3条评论
3个回答

2

使用大型位图时,很有可能会出现内存不足异常...因此,请查看Android博客以了解如何处理:

http://developer.android.com/training/displaying-bitmaps/index.html

并且一定要回收位图。

ImageView mImage;
Drawable toRecycle = mImage.getDrawable();
        if ( toRecycle != null && toRecycle instanceof BitmapDrawable ) {
            if ( ( (BitmapDrawable) mImage.getDrawable() ).getBitmap() != null )
                ( (BitmapDrawable) mImage.getDrawable() ).getBitmap().recycle();
        }

这看起来相当糟糕,平台不应该在位图未被使用时自动回收它吗? - Niko
同时,我的所有位图都在LruCache中,因此我正在寻找一个更通用的答案,其中位图回收发生在一个地方,而不是基于一个ImageView的位图的单一解决方案。 - Niko

2
我最终在这里使用了SoftRefencesBitmap。现在我可以看到GC在快速滚动GridView时释放我的未使用的Bitmaps
测试了将我的LruCache大小设置为完整内存大小,仍然没有OOM。
使用这种方法的惩罚并不是很明显,考虑到它正在绘制非常自定义的图像,我的GridView滚动得相当平稳。

1
一个人应该总是使用SoftReferences,否则gc仍然认为位图仍在使用中,因为它的引用当前被内存中的某些东西持有。 - PravinCG

0

System.gc()不会帮助你,也不能保证任何事情。

如果你确定已经不需要驱逐的位图,并且在任何地方都没有对它们的引用(否则将会捕获“无法绘制已回收的位图”异常),我建议你向LRU缓存添加一个逐出监听器,并在每个被逐出的值上调用bitmap.recycle()。

我记不清默认的LRU缓存是否提供了设置逐出监听器的便捷方法,但如果没有,非常容易扩展它并添加所需的功能。

P.S. 我建议不要使用WeakReferences,因为你会失去对位图和LRU的任何控制。即使你加载了8个漂亮地适合你1/8内存的位图,但屏幕一次只能显示4个(ImageView持有Bitmap的强引用),gc会尽快清除剩下的4个。我真的是指超级快。你必须为你显示的每一行(在ListView的情况下)重新加载位图。每个离开屏幕的位图都必须再次重新加载。


我可以在LruCache上覆盖entryRemoved,但我不知道如何百分之百地检测Bitmap是否被使用,以便我可以调用recycle。那么这不是WeakReference成为好选择的地方吗?因为GC可能会检测到它是垃圾并释放内存。 - Niko
你为什么认为垃圾回收无法跟上,是否出现了OOM? - Alex Orlov
软引用在Android中的行为类似于弱引用,因此使用它们没有意义。 - Alex Orlov
是的,但如果位图是强引用,GC无法快速释放它们,至少使用SoftReferences可以解决OOM问题,但在这里GC会导致性能下降。 - Niko
1
那么,使用具有弱引用的List是您的最佳选择,在这种情况下使用LRU毫无意义,因为您的位图是由引用控制而不是由LRU算法控制的。 - Alex Orlov
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接