34

I am having an OutOfMemory exception with a gallery over 600x800 pixels JPEG's.


The environment

I've been using Gallery with JPG images around 600x800 pixels.

Since my content may be a bit more complex than just images, I have set each view to be a RelativeLayout that wraps ImageView with the JPG.

In order to "speed up" the user experience I have a simple cache of 4 slots that prefetches (in a looper) about 1 image left and 1 image right to the displayed image and keeps them in a 4 slot HashMap.

The platform

I am using AVD of 256 RAM and 128 Heap Size, with a 600x800 screen. It also happens on an Entourage Edge target, except that with the device it's harder to debug.


The problem

I have been getting an exception:

OutofMemoryError: bitmap size exceeds VM budget

And it happens when fetching the fifth image. I have tried to change the size of my image cache, and it is still the same.


The strange thing: There should not be a memory problem

In order to make sure the heap limit is very far away from what I need, I have defined a dummy 8MB array in the beginning, and left it unreferenced so it's immediately dispatched. It is a member of the activity thread and is defined as following

static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }    

The result is that the heap size is nearly 11MB and it's all free. Note I have added that trick after it began to crash. It makes OutOfMemory less frequent.

Now, I am using DDMS. Just before the crash (does not change much after the crash), DDMS shows:

ID  Heap Size   Allocated   Free       %Used    #Objects
1   11.195 MB   2.428 MB    8.767 MB   21.69%   47,156  

And in the detail table it shows:

Type  Count  Total Size   Smallest   Largest   Median    Average
free  1,536  8.739MB      16B        7.750MB   24B       5.825KB

The largest block is 7.7MB. And yet the LogCat says:

ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.

If you mind the relation of the median and the average, it is plausible to assume that most of the available blocks are very small. However, there is a block large enough for the bitmap, it's 7.7M. How come it is still not enough?

Note: I recorded a heap trace. When looking at the amount of data allocated, it does not feel like more than 2M is allocated. It does match the free memory report by DDMS.


  • Could it be that I experience some problem like heap-fragmentation?
  • How do I solve/workaround the problem?
  • Is the heap shared to all threads?
  • Could it be that I interpret the DDMS readout in a wrong way, and there is really no 900K block to allocate? If so, can anybody please tell me where I can see that?

Thanks a lot

Meymann

Meymann
  • 2,500
  • 2
  • 25
  • 22
  • You might get some bites here if you post the code that's actually fetching/decoding/caching/expiring the bitmaps. The problem is almost certainly what you're doing there, not something that requires digging into the internals of heap allocation. – Yoni Samlan Jun 14 '10 at 16:08
  • When debugging code, my prime suspect is always my code. In order to debug it, I obtain hints from the environment. Unfortunately, in this case: A. creating a simple code that reads 3 images of 600x800 at a time into a cache would yield similar results sporadically (checked. in order to make it happen faster, one can add dummy unreferenced arrays), B. I use the tools to probe the problem, but the hints I get from the tools don't coincide. C. The point of this question is getting hints of what's good practice, what is wrong with my conclusions about DDMS readout, and if there is a workaround. – Meymann Jun 14 '10 at 20:07
  • 1
    **A very easy way to reproduce the problem in another form** 1. in your Activity class, add static { byte dummy[]=new byte[4096]; } to force the heap to expand (and remove doubts). 2. create a ViewFlipper. 3. Add about 10 ImageView where each refers to a 600x800 bitmap drawable. 4. When it crashes, look at the DDMS. – Meymann Jun 21 '10 at 11:50

5 Answers5

12

I think there's nothing special in your case. There's just not enough memory. You can't have several 600x800 bitmaps in memory, they consume too much memory. You should save them to SD and load to memory on demand. I think that's exactly what you do.

One thing you should be aware of: DDMS displays java heap memory consumption. But there's also native memory that is not displayed in DDMS. And bitmaps as far as I understand are created in native memory. So DDMS is just a bad tool to track these memory issues. You just need to be sure that you free your memory, that images are collected by Garbage Collector after you don't need them any more.

Garbage Collector works on it's own schedule. That's why you should call Bitmap.recycle() method on bitmaps that you don't need any more. This method frees exactly the native memory that you run out of. This way you don't depend on GC and you can free largest piece of memory as soon as possible.

First of all you should ensure that you don't leak bitmaps.

Here's a nice post on memory allocations, it can help you to dig deeper

Community
  • 1
  • 1
Fedor
  • 42,643
  • 9
  • 75
  • 86
  • Sure, lazy loading from SD is what I've been doing. This is what Gallery is doing. I'm using a 4-slot cache to speed it up, though. Still, there are 3 things I need to understand: 1. How do I force Gallery to dispatch unused images ASAP? 2. How come it's not enough? 800x600x4imagesx3bytesPerPixel = 5M which is smaller than 16M. 3. I wonder why it's so sporadic. Sometimes it works, and it flies in different places all the time. Thanks – Meymann Jul 07 '10 at 07:02
  • 3
    As you use 4-slot cache you can call recycle() when you're removing old image from this cache. – Fedor Jul 07 '10 at 10:15
  • 1
    When getView is called in adapter convertView is passed to you. Actually it's a view that is being recycled. Bitmap instance that is displayed on this view is not needed any more. So I guess you can bitmap displayed on that ImageView and recycle it. That's something close to ASAP approach. – Fedor Jul 07 '10 at 12:05
  • Even without cache, I'm afraid images bound to a gallery do not deallocate as soon as they leave the screen (they don't deallocate even when they are not neighbours of the displayed rectangle, whilst Gallery.getChildCount() says "1"). I use Bitmap Options members such as isPurgable and isInputShareable. It is much better but still not enough. That "adb shell dumpsys meminfo " command shows huge memory allocated behind my back in the native. It's so much more than what the system really needs. Thanks a lot. – Meymann Jul 07 '10 at 13:07
  • Ah, well, convertView is always null. I added an "if" condition to trap it... and it is never "not null" :( Thanks. – Meymann Jul 07 '10 at 13:08
  • That's bad. That indicates there are some more issues in your adapter implementation and it doesn't recycle views. – Fedor Jul 07 '10 at 14:14
  • You may take a look at Fotki application in Market. It displays full-screen photos. Works fine and doesn't fail. We use some custom adapter not gallery, but it's very similar. We don't cache bitmaps in memory just on SD. When we need bitmap we parse it from SD. Views are being recycled. – Fedor Jul 07 '10 at 14:16
5

Not sure if it's an option for you, but have you tried supersampling images Strange out of memory issue while loading an image to a Bitmap object?

Community
  • 1
  • 1
Fedor
  • 42,643
  • 9
  • 75
  • 86
  • 1
    Thanks, but I am afraid it won't do... It kills the quality of the image, as it allows the decoder to be more "sloppy". Consequently, text and small pieces of the image become very smeared... Like JPEG knows how to smear... It actually resembles what happens when taking the JPEG EXIF thumbnail and resizing it to full-scale. Thanks – Meymann Jul 06 '10 at 08:44
0

I Also faced similar issue couple of weeks back and i solved it by scaling down images upto optimal point. I have written complete approach in my blog here and uploaded complete sample project with OOM prone code vs OOM Proof code here.

Shailendra Singh Rajawat
  • 7,936
  • 2
  • 30
  • 38
0

There has been lots of time since I asked that.

The answer should be divided to 2 parts: Pre-Gingerbread: you just use small pictures, use subsampling, maybe one screen size photo, and hope for good. Try to make sure you don't allocate tiny items you can't free before getting a bitmap. Pre-Ginger, the memory for bmps had to be contonuous, and it was not counted in the VM memory. Always look at the logcat. See a lecture about memory from Google IO 2011. Post Ginger it's easier. Since Honeycomb, bitmaps are even counted in your java area. There is no jni area. Always use recycle for bitmaps you don't need. Don't wait for the GC.

Meymann
  • 2,500
  • 2
  • 25
  • 22
0

The question was asked in 2010, when Froyo was fresh. So many things happened since. Before 3.0, bitmaps were allocated in JNI. The memory didn't show in the Dalvik stats. It doesn't have to be monolithic anymore. Before 2.3, memory statistics for JNI were not available (bitmap decoding calls JNI) in logcat. 4.4 evacuated more space. 5.0 the big bang of Art. Back in 2010, Nexus One was high end, with less than 300MB. The budget for an app was around 16MB. Now days, there is about 8 times that memory.

Meymann
  • 2,500
  • 2
  • 25
  • 22