java.lang.OutOfMemoryError: bitmap size exceeds VM

2018-12-31 03:49发布

I developed an application that uses lots of images on Android.

The app runs once, fills the information on the screen (Layouts, Listviews, Textviews, ImageViews, etc) and user reads the information.

There is no animation, no special effects or anything that can fill the memory. Sometimes the drawables can change. Some are android resources and some are files saved in a folder in the SDCARD.

Then the user quits (the onDestroy method is executed and app stays in memory by the VM ) and then at some point the user enters again.

Each time the user enters to the app, I can see the memory growing more and more until user gets the java.lang.OutOfMemoryError.

So what is the best/correct way to handle many images?

Should I put them in static methods so they are not loaded all the time? Do I have to clean the layout or the images used in the layout in a special way?

13条回答
无与为乐者.
2楼-- · 2018-12-31 04:27

I had the same problem just with switching the background images with reasonable sizes. I got better results with setting the ImageView to null before putting in a new picture.

ImageView ivBg = (ImageView) findViewById(R.id.main_backgroundImage);
ivBg.setImageDrawable(null);
ivBg.setImageDrawable(getResources().getDrawable(R.drawable.new_picture));
查看更多
栀子花@的思念
3楼-- · 2018-12-31 04:31

One of the most common errors that I found developing Android Apps is the “java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget” error. I found this error frequently on activities using lots of bitmaps after changing orientation: the Activity is destroyed, created again and the layouts are “inflated” from the XML consuming the VM memory available for bitmaps.

Bitmaps on the previous activity layout are not properly de-allocated by the garbage collector because they have crossed references to their activity. After many experiments I found a quite good solution for this problem.

First, set the “id” attribute on the parent view of your XML layout:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Then, on the onDestroy() method of your Activity, call the unbindDrawables() method passing a reference to the parent View and then do a System.gc().

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

This unbindDrawables() method explores the view tree recursively and:

  1. Removes callbacks on all the background drawables
  2. Removes children on every viewgroup
查看更多
几人难应
4楼-- · 2018-12-31 04:35

I've ran into this exact problem. The heap is pretty small so these images can get out of control rather quickly in regards to memory. One way is to give the garbage collector a hint to collect memory on a bitmap by calling its recycle method.

Also, the onDestroy method is not guaranteed to get called. You may want to move this logic/clean up into the onPause activity. Check out the Activity Lifecycle diagram/table on this page for more info.

查看更多
余生请多指教
5楼-- · 2018-12-31 04:36

To avoid this problem you can use native method Bitmap.recycle() before null-ing Bitmap object (or setting another value). Example:

public final void setMyBitmap(Bitmap bitmap) {
  if (this.myBitmap != null) {
    this.myBitmap.recycle();
  }
  this.myBitmap = bitmap;
}

And next you can change myBitmap w/o calling System.gc() like:

setMyBitmap(null);    
setMyBitmap(anotherBitmap);
查看更多
宁负流年不负卿
6楼-- · 2018-12-31 04:41

I had the exact same problem. After a few testing I found that this error is appearing for large image scaling. I reduced the image scaling and the problem disappeared.

P.S. At first I tried to reduce the image size without scaling the image down. That did not stop the error.

查看更多
素衣白纱
7楼-- · 2018-12-31 04:42

This explanation might help: http://code.google.com/p/android/issues/detail?id=8488#c80

"Fast Tips:

1) NEVER call System.gc() yourself. This has been propagated as a fix here, and it doesn't work. Do not do it. If you noticed in my explanation, before getting an OutOfMemoryError, the JVM already runs a garbage collection so there is no reason to do one again (its slowing your program down). Doing one at the end of your activity is just covering up the problem. It may causes the bitmap to be put on the finalizer queue faster, but there is no reason you couldn't have simply called recycle on each bitmap instead.

2) Always call recycle() on bitmaps you don't need anymore. At the very least, in the onDestroy of your activity go through and recycle all the bitmaps you were using. Also, if you want the bitmap instances to be collected from the dalvik heap faster, it doesn't hurt to clear any references to the bitmap.

3) Calling recycle() and then System.gc() still might not remove the bitmap from the Dalvik heap. DO NOT BE CONCERNED about this. recycle() did its job and freed the native memory, it will just take some time to go through the steps I outlined earlier to actually remove the bitmap from the Dalvik heap. This is NOT a big deal because the large chunk of native memory is already free!

4) Always assume there is a bug in the framework last. Dalvik is doing exactly what its supposed to do. It may not be what you expect or what you want, but its how it works. "

查看更多
登录 后发表回答