6

While resizing large bitmaps for faster image upload to a server I occasionally ran into OutOfMemoryErrors. To prevent this I calculate the required amount of memory and check if it exceeds Runtime.getRuntime().maxMemory() before trying to scale an image.

However, I still run into OOM errors even though the image should fit on the heap easily.

The emulated device (Galaxy SII API 16) gives me a max memory of 67108864 bytes using the above method.

In the following snippet, the heap size is 43975K and only < 15K of that memory is in use. For my ~31K allocation the heap should grow automatically to about 45K which is still not even close to the maximum size of 64 MiB. But as you can see, instead of expanding the heap, the dalvik vm runs out of memory.

10-13 20:35:57.223: D/dalvikvm(1201): GC_FOR_ALLOC freed 505K, 67% free 14692K/43975K, paused 31ms, total 31ms
10-13 20:35:57.223: I/dalvikvm-heap(1201): Forcing collection of SoftReferences for 31961100-byte allocation
10-13 20:35:57.251: D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused 29ms, total 29ms
10-13 20:35:57.251: E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.

I wonder if this can happen on a real device too or if this could be a genymotion bug.

Is the heap guaranteed to expand up to maxMemory()? The JavaDoc for Runtime.getRuntime().freeMemory() says it "may" expand, whatever that means.

I just need a realiable way to calculate the amount of memory I can use, this is how I did it, please correct me if I'm wrong:

long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long availableMemory = maxMemory - usedMemory;

This call causes the OutOfMemoryError:

// outOptions has an appropriate inSampleSize
BitmapFactory.decodeStream(inputStream, null, outOptions);
wkarl
  • 756
  • 1
  • 7
  • 19

3 Answers3

5

Out of memory on a 31961100-byte allocation

Your bitmap is 32M. VM can't allocate 32M linear space to store bitmap. Heap is fragmented, so even if your heap has 32M free space it is not always possible to allocate such linear space. You can try free up as much memory as you can and call GC before decoding stream.

Try to decode your bitmap in more effective way. Or process image in parts. If you tell us why you need this image, we can tell you how to handle it.

Leonidos
  • 10,200
  • 2
  • 26
  • 37
  • I'm already decoding the bitmap with an inSampleSize > 1. Processing the image in parts probably isn't an option either - even if I could save it in parts I'd expect there to be artifacts when stitching it back together (correct me if I'm wrong). What I currently do is scale the image down to a maximum side length of 1920px before uploading it to a server. – wkarl Oct 22 '14 at 21:20
  • Is there a way to find the maximum contiguous space available for allocations? – wkarl Oct 22 '14 at 21:51
  • 2
    Nope. Check this answer: http://stackoverflow.com/questions/3331527/android-resize-a-large-bitmap-file-to-scaled-output-file You also can create separate process to scale image there, it will have same heap size only for image processing. Try to enable userLargeHeap option in manifest. Or upload original image using streams and scale it on server side. – Leonidos Oct 23 '14 at 07:30
  • That is unfortunate, maybe I'll try creating another process then. Is it safe to catch an OutOfMemoryError and upload the file directly in that case? – wkarl Oct 23 '14 at 08:17
  • Another thought: if I run my service in a separate process I'd expect the heap to be not fragmented much initially. Would it be a good idea to allocate one large Bitmap and re-use it for all images using the inBitmap option? I save each bitmap to file before processing the next one. – wkarl Oct 23 '14 at 10:47
  • 1
    You better not catch OutOfMemoryError and try to recover. Google it, there a lot explanations. I don't see any pitfalls in reusing bitmap. But keep in mind that separate process won't give you 100% guarantee that it can fit your image. – Leonidos Oct 23 '14 at 11:01
  • Doesn't work! Tried to use inSampleSize and inJustDecodeBounds in sequence to get the bitmap i want. even with 1400px width image, its still hitting out of memory error, emulator near unusable because of this. but it works well on my 1gb ram moto g lollipop 5.0 – James Tan Sep 22 '15 at 06:15
1

You have a 42MB heap , out of which 14MB is already used, 67% (28M) is free/available

    D/dalvikvm(1201): GC_BEFORE_OOM freed 2K, 67% free 14689K/43975K, paused ...
    E/dalvikvm-heap(1201): Out of memory on a 31961100-byte allocation.

You are trying to allocate ~31M (not 31K) , which is greater than 28M that is available, resulting in OOM.

For details on interpreting dalvikvm memory allocation log message take a look at debugging memory

There is lot of shared memory usage going on in android, to properly calculate per process memory usage refer this SO question

Android best practices on efficient bitmap memory management may be of help

Community
  • 1
  • 1
ashoke
  • 6,182
  • 2
  • 22
  • 25
  • 42MB is the current size of the heap, Runtime.getRuntime().maxMemory() gives me 64 megabytes though. I was under the impression that I could use up to 64 megabytes and the heap would expand up to that size. The StackOverflow post is interesting but it doesn't really answer the question how to reliably calculate how much memory I can allocate safely. – wkarl Oct 19 '14 at 11:04
1

One thing you might try to tweak is build.props file of the ROM.

On Genymotion emulator you can try executing the following via root shell:

cat /system/build.prop | grep dalvik 

and it would display the line with dalvik settings:

dalvik.vm.heapsize=256m
dalvik.vm.lockprof.threshold=500
dalvik.vm.stack-trace-file=/data/anr/traces.txt

And maxmemory is also being reported as 268435456 bytes on the emulator I experimented with.

So, you may try playing with this setting. Also, ensure that the memory allocated in VirtualBox's settings is compatible with these values.

S.D.
  • 28,125
  • 2
  • 75
  • 122
  • 1
    I'm actually not that concerned about the emulator. I was just wondering if the same thing might happen to real users' devices out there which would be bad. – wkarl Oct 23 '14 at 10:51