Android:图库中的内存不足异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3238388/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:15:56  来源:igfitidea点击:

Android: out of memory exception in Gallery

androidmemory-leaksgallery

提问by Rob

My app shows a list of 9 categories and each category displays a Gallery-based coverflow (graciously offered by Neil Davies here) with images of the selected category.

我的应用程序显示了 9 个类别的列表,每个类别显示一个基于画廊的封面流(由 Neil Davies在这里提供),其中包含所选类别的图像。

The images are fetched from the Web, each ranging from 300K to 500K in size, and stored in an arrayList of Drawables. This data is bound to the coverflow using a BaseAdapter (code below).
Every time I exit the coverflow and go back to the list of categories, I clear the arrayList (again, code below).

这些图像是从 Web 上获取的,每个图像的大小从 300K 到 500K 不等,并存储在 Drawables 的 arrayList 中。此数据使用 BaseAdapter(下面的代码)绑定到 Coverflow。
每次我退出coverflow并返回类别列表时,我都会清除arrayList(再次,下面的代码)。

In scenario 1, my arrayList contains 5 Drawables. In this scenario, I can freely browse all the categories and show their images. During my test I cycled through all the categories for 5 times, which seems enough to determine that there is no problem.

在场景 1 中,我的 arrayList 包含 5 个 Drawable。在这种情况下,我可以自由浏览所有类别并显示它们的图像。在我的测试中,我在所有类别中循环了 5 次,这似乎足以确定没有问题。

In scenario 2, my arrayList contains 10 drawables. In this scenario, I get an OutOfMemoryError exception while going through images inside the 5th or 6th categeory:

在场景 2 中,我的 arrayList 包含 10 个可绘制对象。在这种情况下,我在浏览第 5 或第 6 类中的图像时遇到 OutOfMemoryError 异常:

07-13 08:38:21.266: ERROR/dalvikvm-heap(2133): 819840-byte external allocation too large for this process.
07-13 08:38:21.266: ERROR/(2133): VM won't let us allocate 819840 bytes
07-13 08:38:21.277: DEBUG/skia(2133): --- decoder->decode returned false
07-13 08:38:21.287: WARN/dalvikvm(2133): threadid=25: thread exiting with uncaught exception (group=0x4001b188)
07-13 08:38:21.296: ERROR/AndroidRuntime(2133): Uncaught handler: thread Thread-64 exiting due to uncaught exception
07-13 08:38:21.308: ERROR/AndroidRuntime(2133): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)

This doesn't make sense to me. If I am leaking memory I would have expected to crash at some point in scenario 1, but I went through all the categories a substantial number of times and didn't crash. I also used the Memory Analyzer plugin for Eclipse which didn't present any potential culprits.

这对我来说没有意义。如果我正在泄漏内存,我预计会在场景 1 中的某个时刻崩溃,但我已经多次浏览所有类别并且没有崩溃。我还使用了 Eclipse 的内存分析器插件,它没有出现任何潜在的罪魁祸首。

If the system couldn't handle 10 images, like in scenarion 2, I would have expected to crash in the first category, but I crash only after 5 or 6 categories.

如果系统不能处理 10 个图像,就像在场景 2 中一样,我会在第一类中崩溃,但我只在 5 或 6 个类别后崩溃。

The coverflow's adapter functions:

Coverflow 的适配器功能:

public int getCount() {
     return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); 
}

public Object getItem(int position) {    
     return DataManager.getInstance().getImagesBuffer().get(position);
}

public long getItemId(int position) {
     return position;
}

public View getView(int position, View convertView, ViewGroup parent) {      
         ImageView i;
         if (convertView == null)
             i = new ImageView(mContext);
         else
             i = (ImageView)convertView;
         Drawable bufferedImage = (Drawable)getItem(position);
         Log.v("getView", "position: " + position);
         i.setImageDrawable(bufferedImage);

         i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2,
                 Utils.getInstance().getScreenHeight() / 2));
         i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 

         try{
         //Make sure we set anti-aliasing otherwise we get jaggies
         BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
         drawable.setAntiAlias(true);
         }
         catch (Exception e)
         {
             Log.v("getView", "Exception: " + e.toString());
         }
         return i;      
     }

populating the data source upon entry to the category:

在进入类别时填充数据源:

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  String imageUrl = ImageBuffer.getInstance().getImageUrl(i);  
  Log.v("Initial", imageUrl);  
  Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl);  
  ImageBuffer.getInstance().getImages().add(i, fullImage);  

}

clearing the data source when exiting the category (in finish()):

退出类别时清除数据源(在finish()中):

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  if (ImageBuffer.getInstance().images.get(i) != null)  
            {  
                ImageBuffer.getInstance().images.get(i).setCallback(null);  
                ImageBuffer.getInstance().images.set(i, null);  
            }    

}

EDIT:

编辑:

OK, I applied Mathias' LogHeap function on my coverflow and here are some outputs. Prior to loading the first gallery:

好的,我在我的 Coverflow 上应用了 Mathias 的 LogHeap 函数,这里有一些输出。在加载第一个画廊之前:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 6.20MB of 6.28MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (0.00MB free)
DEBUG/dalvikvm(5221): GC freed 4558 objects / 638152 bytes in 84ms
DEBUG/dalvikvm(5221): GC freed 17 objects / 808 bytes in 67ms

After entering the first gallery:

进入第一个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.90MB of 16.89MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 357 objects / 50080 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 353 objects / 27312 bytes in 67ms

After existing the first gallery:

存在第一个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.83MB of 16.89MB (0.11MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 330 objects / 17920 bytes in 77ms
DEBUG/dalvikvm(5221): GC freed 13 objects / 760 bytes in 67ms

After entering the fifth gallery:

进入第五画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.80MB of 23.32MB (0.08MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 842 objects / 99256 bytes in 73ms
DEBUG/dalvikvm(5221): GC freed 306 objects / 24896 bytes in 69ms

After exiting the fifth gallery:

退出第五画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.74MB of 23.32MB (0.11MB free) in [com.example.Coverlow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 331 objects / 18184 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 60 objects / 3128 bytes in 68ms

It seems that more and more memory is allocated when entering a gallery, but very little is released after exiting. Am I not clearing my drawables properly? For each element in my arrayList of drawables I call setCallBack(null) and set the element to null. Is that not enough?
Desperate for any insight.
Thanks

似乎进入画廊时分配的内存越来越多,但退出后释放的内存很少。我没有正确清除我的可绘制对象吗?对于我的可绘制数组列表中的每个元素,我调用 setCallBack(null) 并将元素设置为 null。这还不够吗?
渴望任何见解。
谢谢

回答by Mathias Conradt

The images are fetched from the Web, each ranging from 300K to 500K in size, and stored in an arrayList of Drawables.

这些图像是从 Web 上获取的,每个图像的大小从 300K 到 500K 不等,并存储在 Drawables 的 arrayList 中。

The kb file size of the image you're loading from the web isn't directly relevant. Since they're converted into bitmaps you need to calculate width * height * 4 bytes per image for regular ARGB images. (width and height in px).

您从网络加载的图像的 kb 文件大小并不直接相关。由于它们已转换为位图,因此您需要为常规 ARGB 图像计算每个图像的宽度 * 高度 * 4 字节。(宽度和高度以 px 为单位)。

The bitmaps consume native heap, which usually doesn't show in a hprof. The hprof should only show you the number of objects, i.e. BitmapDrawables or Bitmaps that are left.

位图消耗本机堆,这通常不会显示在 hprof 中。hprof 应该只显示对象的数量,即剩余的 BitmapDrawables 或 Bitmaps。

I use this code in my app to output the current used memory used by the app and native heap:

我在我的应用程序中使用此代码来输出应用程序和本机堆使用的当前已用内存:

public static void logHeap(Class clazz) {
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0);
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0);
    DecimalFormat df = new DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    Log.d(APP, "debug. =================================");
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]");
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)");
    System.gc();
    System.gc();

    // don't need to add the following lines, it's just an app specific handling in my app        
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) {
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

which I call when starting or finishing an activity during development.

我在开发过程中开始或完成活动时调用它。

logHeap(this.getClass());

Here are some informative links - generally there are lots of threads about this topic on here.

这里有一些信息性链接 - 通常这里有很多关于这个主题的主题。

Here's also a useful slide by Romain Guy (Android Framework engineer) about soft references, weak references, simple caches, image handling: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

这里还有 Romain Guy(Android 框架工程师)关于软引用、弱引用、简单缓存、图像处理的有用幻灯片:http: //docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

回答by Fedor

Here are some advices:

以下是一些建议:

  1. Do you use inSampleSize option? It reduces memory consumption if you scale images. Strange out of memory issue while loading an image to a Bitmap object

  2. You should call Bitmap.recycle() when you don't need images any more. I think it's important in your case. Android: OutofMemoryError: bitmap size exceeds VM budget with no reason I can see

  1. 你使用 inSampleSize 选项吗?如果您缩放图像,它会减少内存消耗。将图像加载到位图对象时出现奇怪的内存不足问题

  2. 当您不再需要图像时,您应该调用 Bitmap.recycle()。我认为这对你的情况很重要。Android:OutofMemoryError:位图大小超出虚拟机预算,我看不到任何原因

回答by user2046063

You'd better be well aware that the convertViewin parameters list of getViewis always null. That is to say, gallery does not reuse the old view inside.

你最好清楚地知道convertViewin 的参数列表getView总是null。也就是说,gallery 不会复用里面的旧视图。

回答by Ryan Conrad

The image you are loading in gallery 5 or 6 could be too large to load and it is exceeding the maximum size allow by the VM.

您在图库 5 或 6 中加载的图像可能太大而无法加载,并且超出了 VM 允许的最大大小。