android how to avoid this out of memory error

2019-04-13 08:59发布

I have a lot of images say 500ish spread across 20 or so fragments, the images are all very small and it all loads fine, however i give the user a choice off 3 different kinds of images, and right now if the user changes images i get an OOM error, so i think its the way im handling the images i think i need to recycle the old ones first but im not confident on how to do that can anyone help?

here is how i populate my adapter ive just added two items to simplify things

    private void prepareCardData(){

    CardWriter cardWriter = new CardWriter(getResources().getDrawable(R.drawable.dog),"DOG","dog ");
    cardMakerList.add(cardWriter);

    cardWriter = new CardWriter(getResources().getDrawable(R.drawable.cat_ic),"CAT","cat ");
    cardMakerList.add(cardWriter);

    cardAdapter.notifyDataSetChanged();
}

and this is where i literally just wait for a shared preference change and call a method

private SharedPreferences.OnSharedPreferenceChangeListener listener = new     
SharedPreferences.OnSharedPreferenceChangeListener() {
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if(key.equals("ICONS SELECTED")){
            cardsChoice.setIcons(getActivity());
            cardsToSet();
            System.out.println("OneFrag pref listener");
        }
    }
};
public void cardsToSet(){
    if (cardsChoice.bool_fragTwo == false) {
        if (cardsChoice.group == 1) {
            cardMakerList.clear();
            prepareCardData();
            cardsChoice.bool_fragTwo = true;
        }
        if (cardsChoice.group == 2) {
            cardMakerList.clear();
            prepareSimpleCardData();
            cardsChoice.bool_fragTwo = true;
        }
        if (cardsChoice.group == 3) {
            cardMakerList.clear();
            preparePhotoCardData();
            cardsChoice.bool_fragTwo = true;
        }
    }
}

so as mentioned i think i need to run this in its own thread and clear up and images that arent needed anymore but i think theres more to it then that can anyone help?

here is the stack trace

06-12 17:39:47.878 13189-13219/ss.sealstudios.com.socialstories E/Surface: getSlotFromBufferLocked: unknown buffer: 0x8a12fe70
06-12 17:39:47.985 13189-13189/ss.sealstudios.com.socialstories I/System.out: OneFrag pref listener
06-12 17:39:47.987 13189-13189/ss.sealstudios.com.socialstories I/System.out: OneFrag pref listener
06-12 17:39:48.233 13189-13199/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 265MB to 256MB
06-12 17:39:48.380 13189-13189/ss.sealstudios.com.socialstories I/System.out: OneFrag pref listener
06-12 17:39:48.410 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.410 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.416 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc sticky concurrent mark sweep GC freed 108(4KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 254MB/256MB, paused 303us total 5.710ms
06-12 17:39:48.417 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.433 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 283MB to 256MB
06-12 17:39:48.433 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc partial concurrent mark sweep GC freed 5841(393KB) AllocSpace objects, 2(3MB) LOS objects, 1% free, 251MB/256MB, paused 793us total 15.361ms
06-12 17:39:48.493 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.494 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.502 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc sticky concurrent mark sweep GC freed 17(688B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 545us total 7.748ms
06-12 17:39:48.503 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.526 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.526 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc partial concurrent mark sweep GC freed 23(944B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 494us total 22.482ms
06-12 17:39:48.527 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.567 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.567 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc concurrent mark sweep GC freed 9(12KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 499us total 38.887ms
06-12 17:39:48.568 13189-13189/ss.sealstudios.com.socialstories I/art: Forcing collection of SoftReferences for 2025KB allocation
06-12 17:39:48.568 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.597 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.597 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc concurrent mark sweep GC freed 11(344B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 501us total 28.481ms
06-12 17:39:48.601 13189-13189/ss.sealstudios.com.socialstories W/art: Throwing OutOfMemoryError "Failed to allocate a 2073612 byte allocation with 559872 free bytes and 546KB until OOM"
06-12 17:39:48.603 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.603 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.610 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc sticky concurrent mark sweep GC freed 3(448B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 484us total 6.214ms
06-12 17:39:48.611 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.643 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.643 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 1.131ms total 31.104ms
06-12 17:39:48.644 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.673 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.673 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 631us total 29.306ms
06-12 17:39:48.674 13189-13189/ss.sealstudios.com.socialstories I/art: Forcing collection of SoftReferences for 2025KB allocation
06-12 17:39:48.674 13189-13189/ss.sealstudios.com.socialstories I/art: Starting a blocking GC Alloc
06-12 17:39:48.704 13189-13189/ss.sealstudios.com.socialstories I/art: Clamp target GC heap from 287MB to 256MB
06-12 17:39:48.704 13189-13189/ss.sealstudios.com.socialstories I/art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 255MB/256MB, paused 306us total 29.616ms
06-12 17:39:48.705 13189-13189/ss.sealstudios.com.socialstories W/art: Throwing OutOfMemoryError "Failed to allocate a 2073612 byte allocation with 559872 free bytes and 546KB until OOM"
06-12 17:39:48.709 13189-13189/ss.sealstudios.com.socialstories D/skia: --- allocation failed for scaled bitmap
06-12 17:39:48.709 13189-13189/ss.sealstudios.com.socialstories D/AndroidRuntime: Shutting down VM
06-12 17:39:48.724 13189-13189/ss.sealstudios.com.socialstories E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                  Process: ss.sealstudios.com.socialstories, PID: 13189
                                                                                  java.lang.OutOfMemoryError: Failed to allocate a 2073612 byte allocation with 559872 free bytes and 546KB 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:1080)
                                                                                      at android.content.res.Resources.loadDrawableForCookie(Resources.java:2738)
                                                                                      at android.content.res.Resources.loadDrawable(Resources.java:2643)
                                                                                      at android.content.res.Resources.getDrawable(Resources.java:833)
                                                                                      at android.content.res.Resources.getDrawable(Resources.java:786)
                                                                                      at ss.sealstudios.com.socialstories.TwoFragment.prepareCardData(TwoFragment.java:280)
                                                                                      at ss.sealstudios.com.socialstories.TwoFragment.cardsToSet(TwoFragment.java:245)
                                                                                      at ss.sealstudios.com.socialstories.TwoFragment$2.onSharedPreferenceChanged(TwoFragment.java:236)
                                                                                      at android.app.SharedPreferencesImpl$EditorImpl.notifyListeners(SharedPreferencesImpl.java:479)
                                                                                      at android.app.SharedPreferencesImpl$EditorImpl.apply(SharedPreferencesImpl.java:387)
                                                                                      at android.preference.Preference.tryCommit(Preference.java:1419)
                                                                                      at android.preference.Preference.persistString(Preference.java:1452)
                                                                                      at android.preference.ListPreference.setValue(ListPreference.java:148)
                                                                                      at android.preference.ListPreference.onDialogClosed(ListPreference.java:283)
                                                                                      at android.preference.DialogPreference.onDismiss(DialogPreference.java:395)
                                                                                      at android.app.Dialog$ListenersHandler.handleMessage(Dialog.java:1328)
                                                                                      at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                      at android.os.Looper.loop(Looper.java:148)
                                                                                      at android.app.ActivityThread.main(ActivityThread.java:5527)
                                                                                      at java.lang.reflect.Method.invoke(Native Method)
                                                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
                                                                                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)

1条回答
爷、活的狠高调
2楼-- · 2019-04-13 09:31

if image view hold an instance of bitmap drawable

Drawable drawable = imageView.getDrawable();
if(drawable!=null && BitmapDrawable.class.isAssignableFrom(drawable.getClass())) {
    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
    Bitmap bitmap = bitmapDrawable.getBitmap();
    if(bitmap != null && !bitmap.isRecycled()) bitmap.recycle();
}

but...

If you are loading bitmaps from drawable folders that are not large (large as in megabytes) then you should not really run into a problem.

If you loading assets you need to check if they are optimal for where you display them, for example there is no point in setting an ImageView to an image thats 1024 x 1024 in size if the area you display the image is a size of 64x64.

OOM often is caused by loading images of an unknown size or just simply getting the image sizes wrong as described above, swapping an ImageView frequently will generally not give you an issue with optimal sized images.

You should read some guidance on bitmaps:

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

for your case:

this is essential fragment from stack trace :

FATAL EXCEPTION: main Process: ss.sealstudios.com.socialstories, PID: 13189 
java.lang.OutOfMemoryError: Failed to allocate a 2073612 byte allocation with 559872 free bytes and 546KB 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:1080) 
at android.content.res.Resources.loadDrawableForCookie(Resources.java:2738) 
at android.content.res.Resources.loadDrawable(Resources.java:2643) 
at android.content.res.Resources.getDrawable(Resources.java:833) at android.content.res.Resources.getDrawable(Resources.java:786) 
at ss.sealstudios.com.socialstories.TwoFragment.prepareCardData(TwoFragment.java:280) 

Failed to allocate a 2073612 byte allocation with 559872 free bytes and 546KB until OOM

so generally:

  • in method TwoFragment.prepareCardData(TwoFragment.java:280) befor call to Resources.getDrawable(..) you should get image view and recycle bitmap..
  • in fragment/activity you should also recycle bitmap in onDestroy() method

without more code i'm not able to determine if for example some loop is causing OOM or you are not releasing resources...

you can also do this (scale image) before settings up resource to image view:

Out of Memory Error ImageView issue

caution!!!

Do not use image you already recycled.

You'll get exception: Canvas: trying to use a recycled bitmap

bitmap.recycle() is not strictly required for android >2.3.3. If you still want to reclaim this memory forcefully, you'll have to find a way to check when the bitmap is indeed no longer needed (i.e., Canvas had a chance to finish its drawing operations).

the problem is that you probably making extensive use of Bitmaps (speed of allocation may be greater than speed at which bitmap getting recycled) then you might want to recycle unused bitmaps ASAP.You should call recycle() when you are done using the bitmap.

Always remember don't try to recycle bitmap when it is being shown on the screen.

So:

  • grab reference to old bitmap
  • set new one
  • invalidate view
  • check if old bitmap is not already recycled
  • recycle old one
  • you can also call System.gc(); ^

^Indicates to the VM that it would be a good time to run the garbage collector. Note that this is a hint only. There is no guarantee that the garbage collector will actually be run.

call to ImageView.setImageBitmap(); or similarity will not reclaim memory used by bitmap...

Why? because when u look at the impl of ImageView method especially:

private void updateDrawable(Drawable d) {
    if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
        mRecycleableBitmapDrawable.setBitmap(null);
    }
...


public void setImageResource(@DrawableRes int resId) {
    // The resource configuration may have changed, so we should always
    // try to load the resource even if the resId hasn't changed.
    final int oldWidth = mDrawableWidth;
    final int oldHeight = mDrawableHeight;

    updateDrawable(null);
....

public void setImageBitmap(Bitmap bm) {
    // Hacky fix to force setImageDrawable to do a full setImageDrawable
    // instead of doing an object reference comparison
    mDrawable = null;
    if (mRecycleableBitmapDrawable == null) {
        mRecycleableBitmapDrawable = new ImageViewBitmapDrawable(
                mContext.getResources(), bm);
    } else {
        mRecycleableBitmapDrawable.setBitmap(bm);
    }
    setImageDrawable(mRecycleableBitmapDrawable);
}

See also:

https://developer.android.com/training/displaying-bitmaps/manage-memory.html

查看更多
登录 后发表回答