31

I am sporadically getting an OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB) in one of my apps. What can I do to diagnose this?

  01-09 10:32:02.079: E/dalvikvm(8077): Out of memory: Heap Size=49187KB, Allocated=41957KB, Limit=49152KB
  01-09 10:32:02.079: E/dalvikvm(8077): Extra info: Footprint=48611KB, Allowed Footprint=49187KB, Trimmed=7852KB
  01-09 10:32:02.079: D/skia(8077): --- decoder->decode returned false
  01-09 10:32:02.079: D/AndroidRuntime(8077): Shutting down VM
  01-09 10:32:02.079: W/dalvikvm(8077): threadid=1: thread exiting with uncaught exception (group=0x40a97228)
  01-09 10:32:02.079: E/AndroidRuntime(8077): FATAL EXCEPTION: main
  01-09 10:32:02.079: E/AndroidRuntime(8077): java.lang.OutOfMemoryError: (Heap Size=49187KB, Allocated=41957KB)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:486)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.content.res.Resources.loadDrawable(Resources.java:2044)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.content.res.Resources.getDrawable(Resources.java:675)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.view.View.setBackgroundResource(View.java:11776)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.blsk.bigtoss.ImageLoader.DisplayImage(ImageLoader.java:81)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.blsk.bigtoss.MatchActivity$MatchAsyncTask.onPostExecute(MatchActivity.java:1768)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask.finish(AsyncTask.java:602)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask.access$600(AsyncTask.java:156)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.Handler.dispatchMessage(Handler.java:99)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.os.Looper.loop(Looper.java:156)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at android.app.ActivityThread.main(ActivityThread.java:4987)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at java.lang.reflect.Method.invokeNative(Native Method)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at java.lang.reflect.Method.invoke(Method.java:511)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
  01-09 10:32:02.079: E/AndroidRuntime(8077):   at dalvik.system.NativeStart.main(Native Method)
  01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Process: com.blsk.bigtoss
  01-09 10:32:02.099: E/EmbeddedLogger(1612): App crashed! Package: com.blsk.bigtoss v6 (1.2)
  01-09 10:32:02.129: E/EmbeddedLogger(1612): Application Label: Cricket

This is the line where it is happening:

LinearLayout resultMatchHeaderContainer = new LinearLayout(activity); 

if (!resultImagePath.equals("")) {   
    imageLoader.DisplayImage(resultImagePath,resultMatchHeaderContainer, -1,modifiedHeight, R.drawable.matches_placeholder_result2x);
} else {
    try {
        resultMatchHeaderContainer.setBackgroundResource(R.drawable.matches_placeholder_result2x); 
    } catch (OutOfMemoryError e) {         
        e.printStackTrace();
    }
}
xarlymg89
  • 2,205
  • 2
  • 24
  • 35
NagarjunaReddy
  • 8,500
  • 10
  • 60
  • 94

8 Answers8

44

maybe this help you ?

add manifest

android > v3

<application
    ....
       android:largeHeap="true"> 
SKULL
  • 873
  • 8
  • 13
  • 14
    The use of largeHeap is not recommended in all cases, please use it very cautiously, it might slow other running application, and also impact your app's reactiveness, since the garbage collector with be solicited more often. For more information check this speech from google i/o https://www.youtube.com/watch?v=_CruQY55HOk – Mood Jul 17 '15 at 12:49
  • 4
    Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained. Yet, even when you're confident your app can justify the large heap, you should avoid requesting it to whatever extent possible. see https://developer.android.com/training/articles/memory.html – Adnan Ali Sep 05 '16 at 10:41
37

Common fixes:

1. Fix your contexts:

Try using the appropiate context: For example since a Toast can be seen in many activities instead of in just one, use getApplicationContext() for toasts, and since services can keep running even though an activity has ended start a service with:

Intent myService = new Intent(getApplicationContext(), MyService.class)

Use this table as a quick guide for what context is appropiate: enter image description here

Original article on context here.

2. Check that you're actually finishing your services.

For example I have an intentService that use google location service api. And I forgot to call googleApiClient.disconnect();:

//Disconnect from API onDestroy()
    if (googleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, GoogleLocationService.this);
        googleApiClient.disconnect();
    }

3. Check image and bitmaps usage:

If you are using square's library Picasso I found I was leaking memory by not using the .fit(), that drastically reduced my memory footprint from 50MB in average to less than 19MB:

Picasso.with(ActivityExample.this)                   //Activity context
                .load(object.getImageUrl())           
                .fit()                                //This avoided the OutOfMemoryError
                .centerCrop()                         //makes image to not stretch
                .into(imageView);

4. If you are using broadcast receivers unregister them.

5. If you are using java.util.Observer (Observer pattern):

Make sure to to use deleteObserver(observer);

Community
  • 1
  • 1
CommonSenseCode
  • 18,848
  • 28
  • 112
  • 163
9

You can do following to avoid this.

Drawable drawable = resultMatchHeaderContainer.getDrawable();

if (drawable instanceof BitmapDrawable) {
    BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
    if (bitmapDrawable != null) {
        Bitmap bitmap = bitmapDrawable.getBitmap();

        if (bitmap != null && !bitmap.isRecycled())
           bitmap.recycle();
    }
}

Loading Bitmap in Imageview always been a cause of out of memory issue it is very common so we have to handle imageview and bitmaps very carefully. What you can do is While setting any background bitmap to your imageview first get the drawable and recycle it so that it is removed from memory and then set the new bitmap. This will help to avoid any OOM issue. Further. You can use BitmapFactoryOptions to reduce the size of your bitmap. like:

// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        FileInputStream stream1 = new FileInputStream(f);
        BitmapFactory.decodeStream(stream1, null, o);
        stream1.close();

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_WIDTH
                    || height_tmp / 2 < REQUIRED_HIGHT)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        FileInputStream stream2 = new FileInputStream(f);
        Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
        stream2.close();
        return bitmap;
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
Bhavinkumar Patel
  • 439
  • 11
  • 25
Avtar Guleria
  • 2,016
  • 3
  • 19
  • 31
  • 2
    Why it is downvoted, let me know also the reason for that..Whosover downvoted it please mentioned the reason for it.. – Avtar Guleria Jan 09 '14 at 05:18
  • Although this is a nice idea in theory, it doesn't work so well in practice. The call to Recycle() is merely a hint to the GC to reclaim memory. It may not be reclaimed by the time you create your new image and as such may still cause your application to crash. More to the point though, the GC will automatically reclaim the memory when over the next few CG cycles. – TheIT Jan 09 '14 at 05:37
  • 2
    Really why somebody downvoted ,he/she is real nerd ..Most of the time it works ..It worked for me ..in my project.. – Shakeeb Ayaz Jan 09 '14 at 05:45
  • i was about to answer the same so +1 – Shakeeb Ayaz Jan 09 '14 at 05:47
  • Thanks. Shakeeb Ayaz. @TheIT, Do you mean that recycle method on bitmap is not useful or what if that then in Which situation we should use it? – Avtar Guleria Jan 09 '14 at 05:51
  • I'm not fully aware of the exact details of it's functionality, but I believe it simply increases the object's GC priority. – TheIT Jan 09 '14 at 20:22
1

Before loading images into memory compress your images using

Bitmap original = BitmapFactory.decodeStream(getAssets().open("1024x768.jpg"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.PNG, 100, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));

Log.e("Original   dimensions", original.getWidth()+" "+original.getHeight());
Log.e("Compressed dimensions", decoded.getWidth()+" "+decoded.getHeight());  

If you are geting your bitmap from a resource, in which case the bitmap dimension will depend on the phone screen density

Bitmap bitmap=((BitmapDrawable)getResources().getDrawable(R.drawable.img_1024x768)).getBitmap();
Log.e("Dimensions", bitmap.getWidth()+" "+bitmap.getHeight());
rajahsekar
  • 890
  • 11
  • 25
  • For your second point, I would look at loading the bitmap at a reduced scale if possible using BitmapFactory.Options and the BitmapFactory.decodeResource() method. See http://developer.android.com/training/displaying-bitmaps/load-bitmap.html for more details. – TheIT Jan 09 '14 at 05:41
1

final Bitmap smile = BitmapFactory.decodeResource(getResources(), R.drawable.emo_im_happy);

Call

String pathname=BitMapToString(smile);

and then call

setImagesNew(linearview,pathname,activity);

...

public String BitMapToString(Bitmap bitmap) { 
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
             bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); 
        byte[] b = baos.toByteArray();
      String temp = Base64.encodeToString(b, Base64.DEFAULT);
      return temp;
 }


 public static void setImagesNew(LinearLayout linearLayout, String pathName,
                Activity activity) {

            Bitmap bmp = decodeSampledBitmapFromResource(pathName,
                    getDeviceWidth(activity), getDeviceHeight(activity));

linearLayout.setBackgroundDrawable(bmp);


            bmp = null;
            System.gc();
            Runtime.getRuntime().gc();

        }
    public static Bitmap decodeSampledBitmapFromResource(String pathName,
            int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(pathName, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(pathName, options);
    }

    public static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    @SuppressLint("NewApi")
    public static int getDeviceWidth(Activity activity) {
        int deviceWidth = 0;

        Point size = new Point();
        WindowManager windowManager = activity.getWindowManager();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            windowManager.getDefaultDisplay().getSize(size);
            deviceWidth = size.x;
        } else {
            Display display = windowManager.getDefaultDisplay();
            deviceWidth = display.getWidth();
        }
        return deviceWidth;
    }

    @SuppressLint("NewApi")
    public static int getDeviceHeight(Activity activity) {
        int deviceHeight = 0;

        Point size = new Point();
        WindowManager windowManager = activity.getWindowManager();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            windowManager.getDefaultDisplay().getSize(size);
            deviceHeight = size.y;
        } else {
            Display display = windowManager.getDefaultDisplay();
            deviceHeight = display.getHeight();
        }
        return deviceHeight;
    }

please put all function in your activity and call only setImageNew() and pass parameter in imageview ,sdcardpathname and activity

I hope it will not crash after you implement this code. because I arise same problem as you..

USER9561
  • 1,040
  • 2
  • 13
  • 32
dipali
  • 10,261
  • 5
  • 21
  • 50
  • final Bitmap smile = BitmapFactory.decodeResource(getResources(), R.drawable.emo_im_happy); – dipali Jan 09 '14 at 06:32
  • public String BitMapToString(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] b = baos.toByteArray(); String temp = Base64.encodeToString(b, Base64.DEFAULT); return temp; } – dipali Jan 09 '14 at 06:32
  • call BitMapToString(smile); – dipali Jan 09 '14 at 06:32
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44853/discussion-between-nagarjunareddy-and-dipali) – NagarjunaReddy Jan 09 '14 at 07:07
1

This could occur for several reasons, you might be keeping references to other parts of your code too long. You might be loading to large bitmaps which together with holding on to many references gives you OOM, etc.

Normally when an OOM occurs a hprof (snapshot of the Heap) is created on the root of the sdcard (or internal storage if sdcard does not exist), which can be read by tools like Eclipse MAT (included in the android tools if you use Eclipse). First one might need to convert the hprof with hprof-conv tool. Here's one tutorial of how to use Eclipse MAT: Investigating Your RAM Usage. The leak suspects report is a good first read when hprof is loaded in Eclipse MAT

After profiling you you could read up on how to load images effectively from Displaying Bitmaps Efficiently

There's also several popular image loading libraries such as universal image loader and picasso available, that do what you need with ease.

Magnus
  • 1,453
  • 11
  • 14
  • @NagarjunaReddy updated this answer with some more ideas what you could do. I would start off by analyzing the `hprof` to see if you're holding on to too many refs of some kind, if that's not the case you could consider using some image libraries which are caching images and adapting them to your needed view sizes needs with ease. – Magnus Jan 09 '14 at 20:06
0

This may happen if the bitmap resource is not disposed correctly. It is better to read the dimensions to see if it fits the memory. http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Selvan
  • 1
0

I solved the same problem by:

  • reduce image dimension from 5000x2500 to 320x240 - 1080x720 (few drawabls folders)
  • converting the PNG image to WEBP format. Its size has decreased 50 times (from 1.2 MB to 30 KB).

https://developer.android.com/studio/write/convert-webp#convert_images_to_webp

Gogi Bobina
  • 1,008
  • 1
  • 8
  • 24