Rectangle 27 1

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


public static void logMemoryStats() {
        String text = "";
        text += "\nLoadedClassCount="               + toMib(android.os.Debug.getLoadedClassCount());
        text += "\nGlobalAllocSize="                + toMib(android.os.Debug.getGlobalAllocSize());
        text += "\nGlobalFreedSize="                + toMib(android.os.Debug.getGlobalFreedSize());
        text += "\nGlobalExternalAllocSize="        + toMib(android.os.Debug.getGlobalExternalAllocSize());
        text += "\nGlobalExternalFreedSize="        + toMib(android.os.Debug.getGlobalExternalFreedSize());
        text += "\nEpicPixels="                     + toMib(EpicBitmap.getGlobalPixelCount()*4);
        text += "\nNativeHeapSize="                 + toMib(android.os.Debug.getNativeHeapSize());
        text += "\nNativeHeapFree="                 + toMib(android.os.Debug.getNativeHeapFreeSize());
        text += "\nNativeHeapAllocSize="            + toMib(android.os.Debug.getNativeHeapAllocatedSize());
        text += "\nThreadAllocSize="                + toMib(android.os.Debug.getThreadAllocSize());

        text += "\ntotalMemory()="                  + toMib(Runtime.getRuntime().totalMemory());
        text += "\nmaxMemory()="                    + toMib(Runtime.getRuntime().maxMemory());
        text += "\nfreeMemory()="                   + toMib(Runtime.getRuntime().freeMemory());

        android.app.ActivityManager.MemoryInfo mi1 = new android.app.ActivityManager.MemoryInfo();
        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        am.getMemoryInfo(mi1);
        text += "\napp.mi.availMem="                + toMib(mi1.availMem);
        text += "\napp.mi.threshold="               + toMib(mi1.threshold);
        text += "\napp.mi.lowMemory="               + mi1.lowMemory;

        android.os.Debug.MemoryInfo mi2 = new android.os.Debug.MemoryInfo();        
        Debug.getMemoryInfo(mi2);
        text += "\ndbg.mi.dalvikPrivateDirty="      + toMib(mi2.dalvikPrivateDirty);
        text += "\ndbg.mi.dalvikPss="               + toMib(mi2.dalvikPss);
        text += "\ndbg.mi.dalvikSharedDirty="       + toMib(mi2.dalvikSharedDirty);
        text += "\ndbg.mi.nativePrivateDirty="      + toMib(mi2.nativePrivateDirty);
        text += "\ndbg.mi.nativePss="               + toMib(mi2.nativePss);
        text += "\ndbg.mi.nativeSharedDirty="       + toMib(mi2.nativeSharedDirty);
        text += "\ndbg.mi.otherPrivateDirty="       + toMib(mi2.otherPrivateDirty);
        text += "\ndbg.mi.otherPss"                 + toMib(mi2.otherPss);
        text += "\ndbg.mi.otherSharedDirty="        + toMib(mi2.otherSharedDirty);

        EpicLog.i("ArchPlatform[android].logStats() - " + text);
    }

@dpk - toMib is a helper that formats large byte counts into more friendly "12.3 MiB" ... MiB is the more pedantic form of MB that implies base-1024 SI units instead of base-1000. I probably didn't realize I used it and didn't paste it. You could remove it or make it your own favorite format at no loss to the intention of the code.

The code to print all that out:

The limit is based on NativeHeapAllocSize vs. maxMemory(). You will see below that I'm crashing allocating ~1 MiB while I'm at 22.0 MiB / 24 MiB. The limit is an UPPER BOUND on how much memory you can allocate. This is what threw me for a while. The crash happens significantly before you hit the limit. Thus, the need for a "memoryPad" value in the solution, as trying to alloc 23.999 MiB / 24 MiB will result in a crash nearly 100% of the time. So if the limit is 24 MiB, how much can you safely use?? Unknown. 20 MiB seems to work. 22 MiB seems to work. I'm nervous pushing any closer than that. The ammount varies depending on how fragmented the malloc memory space is in the native process. And of course, there is no way to measure any of this, so err on the safe side.

Note
Rectangle 27 1

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds=true;
        BitmapFactory.decodeFile(path,bmpFactoryOptions);
        if ( (runInSafeMemoryMode()) && (!Manager.checkBitmapFitsInMemory(bmpFactoryOptions.outWidth, bmpFactoryOptions.outHeight, 2)) ){
            Log.w(TAG,"Aborting bitmap load for avoiding memory crash");
            return null;        
        }
/**
 * Checks if a bitmap with the specified size fits in memory
 * @param bmpwidth Bitmap width
 * @param bmpheight Bitmap height
 * @param bmpdensity Bitmap bpp (use 2 as default)
 * @return true if the bitmap fits in memory false otherwise
 */
public static boolean checkBitmapFitsInMemory(long bmpwidth,long bmpheight, int bmpdensity ){
    long reqsize=bmpwidth*bmpheight*bmpdensity;
    long allocNativeHeap = Debug.getNativeHeapAllocatedSize();


    final long heapPad=(long) Math.max(4*1024*1024,Runtime.getRuntime().maxMemory()*0.1);
    if ((reqsize + allocNativeHeap + heapPad) >= Runtime.getRuntime().maxMemory())
    {
        return false;
    }
    return true;

}

@vomitcuddle What do you have to do then post Honeycomb in order to update this code to work appropriately. Is it just a different call into Debug.getNativeHeapAllocatedSize() and replacing it with something else?

Also Addev, what are the constants that you have defined above. Are the 1024 components meant to represent a texture size, the 4 to represent the Config number of bytes per pixel and the 0.1 just an arbitrary number so that we don't load it in more than a tenth of available memory?

Here is an example of use

I tried... but if I use checkBitmapFitsInMemory() it returns false and if I don't use checkBitmapFitsInMemory() the app loads bitmap without any crash. My logged values: bmpwidth=93, bmpheight=131, bmpdensity=2, reqsize=24366, heapPad=4194304, allocNativeHeap=33150304, maxMemory=33554432. Any idea about how to write a similar method, but that works better?

This code doesn't work on Honeycomb and above, since Bitmaps aren't stored on the native heap anymore.

This is very similar to the code I ended up using. Instead of returning true / false, I use bitmap.recycle() to free up space...

Note
Rectangle 27 1

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds=true;
        BitmapFactory.decodeFile(path,bmpFactoryOptions);
        if ( (runInSafeMemoryMode()) && (!Manager.checkBitmapFitsInMemory(bmpFactoryOptions.outWidth, bmpFactoryOptions.outHeight, 2)) ){
            Log.w(TAG,"Aborting bitmap load for avoiding memory crash");
            return null;        
        }
/**
 * Checks if a bitmap with the specified size fits in memory
 * @param bmpwidth Bitmap width
 * @param bmpheight Bitmap height
 * @param bmpdensity Bitmap bpp (use 2 as default)
 * @return true if the bitmap fits in memory false otherwise
 */
public static boolean checkBitmapFitsInMemory(long bmpwidth,long bmpheight, int bmpdensity ){
    long reqsize=bmpwidth*bmpheight*bmpdensity;
    long allocNativeHeap = Debug.getNativeHeapAllocatedSize();


    final long heapPad=(long) Math.max(4*1024*1024,Runtime.getRuntime().maxMemory()*0.1);
    if ((reqsize + allocNativeHeap + heapPad) >= Runtime.getRuntime().maxMemory())
    {
        return false;
    }
    return true;

}

@vomitcuddle What do you have to do then post Honeycomb in order to update this code to work appropriately. Is it just a different call into Debug.getNativeHeapAllocatedSize() and replacing it with something else?

Also Addev, what are the constants that you have defined above. Are the 1024 components meant to represent a texture size, the 4 to represent the Config number of bytes per pixel and the 0.1 just an arbitrary number so that we don't load it in more than a tenth of available memory?

Here is an example of use

I tried... but if I use checkBitmapFitsInMemory() it returns false and if I don't use checkBitmapFitsInMemory() the app loads bitmap without any crash. My logged values: bmpwidth=93, bmpheight=131, bmpdensity=2, reqsize=24366, heapPad=4194304, allocNativeHeap=33150304, maxMemory=33554432. Any idea about how to write a similar method, but that works better?

This code doesn't work on Honeycomb and above, since Bitmaps aren't stored on the native heap anymore.

This is very similar to the code I ended up using. Instead of returning true / false, I use bitmap.recycle() to free up space...

Note
Rectangle 27 1

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


public static void logMemoryStats() {
        String text = "";
        text += "\nLoadedClassCount="               + toMib(android.os.Debug.getLoadedClassCount());
        text += "\nGlobalAllocSize="                + toMib(android.os.Debug.getGlobalAllocSize());
        text += "\nGlobalFreedSize="                + toMib(android.os.Debug.getGlobalFreedSize());
        text += "\nGlobalExternalAllocSize="        + toMib(android.os.Debug.getGlobalExternalAllocSize());
        text += "\nGlobalExternalFreedSize="        + toMib(android.os.Debug.getGlobalExternalFreedSize());
        text += "\nEpicPixels="                     + toMib(EpicBitmap.getGlobalPixelCount()*4);
        text += "\nNativeHeapSize="                 + toMib(android.os.Debug.getNativeHeapSize());
        text += "\nNativeHeapFree="                 + toMib(android.os.Debug.getNativeHeapFreeSize());
        text += "\nNativeHeapAllocSize="            + toMib(android.os.Debug.getNativeHeapAllocatedSize());
        text += "\nThreadAllocSize="                + toMib(android.os.Debug.getThreadAllocSize());

        text += "\ntotalMemory()="                  + toMib(Runtime.getRuntime().totalMemory());
        text += "\nmaxMemory()="                    + toMib(Runtime.getRuntime().maxMemory());
        text += "\nfreeMemory()="                   + toMib(Runtime.getRuntime().freeMemory());

        android.app.ActivityManager.MemoryInfo mi1 = new android.app.ActivityManager.MemoryInfo();
        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        am.getMemoryInfo(mi1);
        text += "\napp.mi.availMem="                + toMib(mi1.availMem);
        text += "\napp.mi.threshold="               + toMib(mi1.threshold);
        text += "\napp.mi.lowMemory="               + mi1.lowMemory;

        android.os.Debug.MemoryInfo mi2 = new android.os.Debug.MemoryInfo();        
        Debug.getMemoryInfo(mi2);
        text += "\ndbg.mi.dalvikPrivateDirty="      + toMib(mi2.dalvikPrivateDirty);
        text += "\ndbg.mi.dalvikPss="               + toMib(mi2.dalvikPss);
        text += "\ndbg.mi.dalvikSharedDirty="       + toMib(mi2.dalvikSharedDirty);
        text += "\ndbg.mi.nativePrivateDirty="      + toMib(mi2.nativePrivateDirty);
        text += "\ndbg.mi.nativePss="               + toMib(mi2.nativePss);
        text += "\ndbg.mi.nativeSharedDirty="       + toMib(mi2.nativeSharedDirty);
        text += "\ndbg.mi.otherPrivateDirty="       + toMib(mi2.otherPrivateDirty);
        text += "\ndbg.mi.otherPss"                 + toMib(mi2.otherPss);
        text += "\ndbg.mi.otherSharedDirty="        + toMib(mi2.otherSharedDirty);

        EpicLog.i("ArchPlatform[android].logStats() - " + text);
    }

@dpk - toMib is a helper that formats large byte counts into more friendly "12.3 MiB" ... MiB is the more pedantic form of MB that implies base-1024 SI units instead of base-1000. I probably didn't realize I used it and didn't paste it. You could remove it or make it your own favorite format at no loss to the intention of the code.

The code to print all that out:

The limit is based on NativeHeapAllocSize vs. maxMemory(). You will see below that I'm crashing allocating ~1 MiB while I'm at 22.0 MiB / 24 MiB. The limit is an UPPER BOUND on how much memory you can allocate. This is what threw me for a while. The crash happens significantly before you hit the limit. Thus, the need for a "memoryPad" value in the solution, as trying to alloc 23.999 MiB / 24 MiB will result in a crash nearly 100% of the time. So if the limit is 24 MiB, how much can you safely use?? Unknown. 20 MiB seems to work. 22 MiB seems to work. I'm nervous pushing any closer than that. The ammount varies depending on how fragmented the malloc memory space is in the native process. And of course, there is no way to measure any of this, so err on the safe side.

Note
Rectangle 27 0

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


  • And of course free the memory of old bitmaps
  • Indicate the desired size for the bitmap before decoding it. Check that links for more info:
  • use the onLowMemory() of the Application class for freeing some memory avoiding the crash.

I tried Application.onLowMemory(). Unfortunately, it never gets called in my repro of the segfault crash.

The limit varies over each device (Use the 3rd link if you want to load the bitmap as is) or here you have some tricks to avoid that issue like:

Note
Rectangle 27 0

out of memory Android Bitmap Limit Preventing java.lang.OutOfMemory?


  • And of course free the memory of old bitmaps
  • Indicate the desired size for the bitmap before decoding it. Check that links for more info:
  • use the onLowMemory() of the Application class for freeing some memory avoiding the crash.

I tried Application.onLowMemory(). Unfortunately, it never gets called in my repro of the segfault crash.

The limit varies over each device (Use the 3rd link if you want to load the bitmap as is) or here you have some tricks to avoid that issue like:

Note