android createBitmap OOM when ((freeMemory > bitma

2020-07-22 16:52发布

问题:

On Android 2.2, the following program produces an OOM. In summary, the program does the following:

  1. Allocates a big array that requires native heap to grow close to its maximum size.
  2. Garbage collects the array.
  3. Attempts to create a bitmap of size larger than the remaining native free heap.

Why does this fail with an OOM? It's as if the native heap will only allocate memory for bitmaps in memory previously unallocated.

The output appears below the program. Thanks in advance.

public class OomTest extends Activity {
    private static final String OOM_TEST = "OomTest";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        printDiag();
        Log.i(OOM_TEST, "Allocating big int array.");
        int[] bigIntArray = new int[11000000];
        printDiag();
        bigIntArray = null;
        Log.i(OOM_TEST, "Garbage collecting.");
        System.gc();
        printDiag();
        Log.i(OOM_TEST, "Creating 4 meg bitmap bitmap.");
        Bitmap bitmap = Bitmap.createBitmap(1000, 1000, Config.ARGB_8888);
        printDiag();        
        Log.i(OOM_TEST, "Done.");
    }

    private void printDiag() {
        Log.i(OOM_TEST, "maxMemory: " + Runtime.getRuntime().maxMemory());
        Log.i(OOM_TEST, "totalMemory: " 
              + Runtime.getRuntime().totalMemory());
        Log.i(OOM_TEST, "freeMemory: " 
              + Runtime.getRuntime().freeMemory());
        Log.i(OOM_TEST, "nativeHeapSize: " 
              + android.os.Debug.getNativeHeapSize());
        Log.i(OOM_TEST, "nativeHeapAllocatedSize: " 
              + android.os.Debug.getNativeHeapAllocatedSize());
        Log.i(OOM_TEST, "nativeHeapFreeSize: " 
              + android.os.Debug.getNativeHeapFreeSize());
    }
}

Here's the output:

I/ActivityManager( 2501): Start proc com.example.android.oomTest for activity
    com.example.android.oomTest/.OomTest: pid=25242 uid=10031 gids={1015}
I/OomTest (25242): maxMemory: 50331648
I/OomTest (25242): totalMemory: 2953200
I/OomTest (25242): freeMemory: 479512
I/OomTest (25242): nativeHeapSize: 3600384
I/OomTest (25242): nativeHeapAllocatedSize: 3585800
I/OomTest (25242): nativeHeapFreeSize: 14584
I/OomTest (25242): Allocating big int array.
D/dalvikvm(25242): GC_FOR_MALLOC freed 634 objects / 51344 bytes in 48ms
I/dalvikvm-heap(25242): Grow heap (frag case) to 44.573MB for 44000016-byte allocation
D/dalvikvm(25242): GC_FOR_MALLOC freed 34 objects / 1992 bytes in 60ms
I/OomTest (25242): maxMemory: 50331648
I/OomTest (25242): totalMemory: 46997472
I/OomTest (25242): freeMemory: 575224
I/OomTest (25242): nativeHeapSize: 3600384
I/OomTest (25242): nativeHeapAllocatedSize: 3578408
I/OomTest (25242): nativeHeapFreeSize: 23560
I/OomTest (25242): Garbage collecting.
D/dalvikvm(25242): GC_EXPLICIT freed 65 objects / 44002952 bytes in 52ms
I/OomTest (25242): maxMemory: 50331648
I/OomTest (25242): totalMemory: 46997472
I/OomTest (25242): freeMemory: 44576440
I/OomTest (25242): nativeHeapSize: 3600384
I/OomTest (25242): nativeHeapAllocatedSize: 3575784
I/OomTest (25242): nativeHeapFreeSize: 24600
I/OomTest (25242): Creating 4 meg bitmap bitmap.
E/dalvikvm-heap(25242): 4000000-byte external allocation too large for this process.
E/GraphicsJNI(25242): VM won't let us allocate 4000000 bytes
D/AndroidRuntime(25242): Shutting down VM
W/dalvikvm(25242): threadid=1: thread exiting with uncaught exception (group=0x4001d7d0)
E/AndroidRuntime(25242): FATAL EXCEPTION: main
E/AndroidRuntime(25242): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime(25242):        at android.graphics.Bitmap.nativeCreate(Native Method)
E/AndroidRuntime(25242):        at     android.graphics.Bitmap.createBitmap(Bitmap.java:468)
E/AndroidRuntime(25242):        at com.example.android.oomTest.OomTest.onCreate(OomTest.java:26)

回答1:

I think you're deep into undocumented behavior here-- you wouldn't be able to count on any answer you get being consistent across different Android versions or devices.

My guess is that the system is granting your app a maximum process size. The process size consists of the dalvik heap and the native heap (plus some overhead, of course). You've expanded the dalvik heap to the maximum. Garbage collecting created space in the dalvik heap, but all the process's space is allocated to the dalvik heap and is therefore unavailable to the native heap, so there isn't room in the native heap to accomodate the bitmap object.

I'm fairly certain this behavior differed in previous (or just other?) releases of Android, where the dalvik heap could not expand beyond about half of the total process size limit.