I am making Image gallery app with various types of image in term of resolution and size.
As per my observation, when app try to load large image its throws OutOfMemory.
How can i prevent app from OutofMemory?
Is there any way to get notification before app get crash because of OutOfMemory?
How can i know app going to reach heap capacity?
How can i prevent app from OutofMemory?
Allocate less memory. For example, with images, use things like inSampleSize on BitmapFactory.Options to only load into memory what you need, not the whole image.
Also, if your images will be the same resolution, use inBitmap to reuse already-allocated Bitmap objects, rather than let them get garbage-collected.
Is there any way to get notification before app get crash because of OutOfMemory?
No, because you are not out of memory.
How can i know app going to reach heap capacity?
You are not reaching "heap capacity".
Dalvik's garbage collector is a non-compacting (or non-moving) garbage collector, and so over time your heap becomes fragmented. OutOfMemoryError means that you are trying to allocate something for which there is no single free block big enough. I wrote a blog post that explains this a bit more and explains how the new ART runtime will help in this regard in the future.
getMemoryClass() gives you an estimation of how much memory you have available in your application.
getLargeMemoryClass() gives you an estimation of the large heap size you can allocate to your application.
So debug your application first to know where does it throw an exception exactly (line) then add logs to see how much memory you got.
Related
I'm using vectordrawable for displaying images. The images are all resources which are bundled with the app (apk). My problem is, that the memory on every new activity massivly get higher until the app crashes with an OutOffMemoryException.
java.lang.OutOfMemoryError
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:903)
at android.graphics.Bitmap.createBitmap(Bitmap.java:880)
at android.graphics.Bitmap.createBitmap(Bitmap.java:847)
I've looked into MAT for memory leaks, but did'nt find out any more than the bitmap errors.
What are the efficients way to display vectordrawables? Maybe the general architecture of my app isnt right with the activity lifecycle?
I didn't find any informations about the common memory ussage of other apps (facebook, aibnb, whatsapp,..). My usage is around 40-70MB.
There are a few things to be careful about when dealing with images:
Are you loading your images every time when you show them? This can be a potentially very memory consuming operation. It's better to load the images into memory once and then reuse them across the app.
Are you cleaning the memory occupied by the images you no longer use? If you are sure that an image is no longer needed, you should clean the allocated bitmap memory by calling bitmap.recycle(). This invalidates the bitmap and frees all the occupied memory, making it available for other operations.
Are the images too large? There is a limit for the maximum size of a single image. Trying to load a bigger image will also cause an OutOfMemoryError, even though memory can be available. In that case, you may want to optimise the image files that you are trying to load.
Go though your app and check for those potential problems one by one. Also, there are good profiling tools available for the Android platform. They can show you potential problems with the memory management, like excessive memory allocation, etc.
Good luck.
OOM happens when the heap gets full. But what if we increased the size of the RAM of the device, will that help prevent OOM?
I tried to look at tutorials on how to prevent OOM , which recommended multi-threading and trimming down the size of the bitmap. But I still get OOM errors.
But what if we increased the size of the RAM on the device, will that help prevent OOM?
Yes and no. The answer is "it depends on why you are running out of memory."
If you are trying to manipulate a 2GB Bitmap on a 1GB device, then yes, adding a few extra GB of RAM will solve your problem.
If you have a rogue operation that is constantly allocating new Objects, then adding additional memory will just delay the OOM Exception. Extra RAM is not a long term solution to poor memory management.
You need to work to identify why you are running out of memory first. If the issue is that you really do need more RAM (which is fairly unlikely), then go ahead and add more. If the problem is that you are unnecessarily allocating memory or leaking objects, then fix those problems first.
With Bitmaps specifically, you often don't need the full quality image. If your image is stored at 1080p resolution but the device is only a 480p screen, then loading the entire image into memory is a waste of space since most of the image won't be used anyway.
For several apps that I have tested, it seems that the Heap memory usage is quite high even at the start of the application. For example, I have an app that is supposed to show an image from assets on an ImageView. But, even before loading the image, used Heap, is about 95%. When I load and display the image, it still fine, it increases used memory to about 98%. But, if I create a copy of the bitmap, then app crashes with out of memory error.
Again, my main question is why the amount of free memory is so low right at the start of an application, before any processing is done.
The reason you are seeing such high amount of memory usage percentage is because the heap hasn't grown to its maximum size. This means that even though you see 98% of usage that refers to your current heap size and not the total amount that you can use.
At runtime, the heap grows dynamically as Dalvik VM needs more memory. It is common that you start with a relatively small heap and with each GC if needed Dalvik will request more memory.
Now for your OutOfMemoryError it can be a lot of things. I would suggest sampling your bitmap at runtime using the BitmapFactory class inSampleSize
Please note I do NOT have a memory leak. My question is about a subtler issue.
I recently wrote an android app which does image processing. The image is loaded as a Bitmap, then copied out in pixels, processed in a way that uses lots of memory (think Fourier transforms in floating point representations and stuff), then converted back into a bitmap and saved.
The problem is, through at least android OS 2.3, the total memory limitation (typically 16MB) is combined java and (externally stored) Bitmaps, and the java high water mark doesn't go down (that I can discern) even when the memory is free (successfully GC'd), which means when I go to allocate the final Bitmap, I am often "out of memory" even though by that point I have freed (and GC'd) most of the space. I.e., I never need the full 16MB at once, but the space left for Bitmaps appears to be 16MB minus the MAX historical java heap usage (as opposed to current usage).
I watched a tech talk by one of the android developers about memory issues and he implied this problem has been fixed in subsequent versions of the OS (they moved Bitmap memory into the java heap space), but in the meantime most of the people wanting to use my app are running 2.2 or 2.3.
Long story short, I am wondering if the java heap is ever compacted (de-fragmented, in effect) so that the high-water mark shrinks (and if so, how to make it happen)?
If not, then does anybody have another suggestion how to deal with this problem?
Long story short, I am wondering if the java heap is ever compacted (de-fragmented, in effect) so that the high-water mark shrinks (and if so, how to make it happen)?
Whatever its behavior is, it most certainly is not under your control.
If not, then does anybody have another suggestion how to deal with this problem?
Ideally, reuse your own Bitmaps. You don't indicate what "processed in a way that uses lots of memory" really is. However, if it does not change the dimensions or bit depth of the image, copy the data back out to the original Bitmap rather than allocating a fresh one, if you can.
Image processing on Android 2.x is one of the few places where I can see justifying using multiple processes. You will add overhead for schlepping the image data between processes, but the other process has its own heap (Java and native), so this may give you more "elbow room".
So far, no indication that there is any way to compact the memory.
Here is my workaround, which is suboptimal but much better than the behavior before:
I now intentionally hold on to the original Bitmap while I am doing my processing, and then recycle() and null it, and GC(), but not until just before allocating my output Bitmap.
What this does is reserve external (Bitmap) space, and cause my application to run out of java heap (during processing, before calling recycle()), which I can at least catch and handle by retrying on a smaller image. (Before, everything seemed to be fine until I tried to save, but by then it was too late and with no way to recover.)
Technically this limits my max image size to less than I should be able to do with the allotted memory (because I need to reserve space in the heap and external at the same time when in truth I never need both together), but at least I can still handle a reasonable image size.
What was happening before is I would free and recycle the Bitmap early which allowed the high water mark on the java heap to use up essentially all of my memory allotment, meaning from that point forward I couldn't open or create any more Bitmaps at all (other than tiny thumbnail sizes sometimes).
Imo, this is a major bug in the way android handles Bitmap memory, but I believe it is fixed in newer versions of the OS so hopefully I can disable this workaround conditional on the OS release.
I'm assuming that you already call Bitmap.recycle() but it's the only thing I remembered and you didn't talk about.
I've been doing a lot of searching and I know a lot of other people
are experiencing the same OOM memory problems with BitmapFactory. My
app only shows a total memory available of 4MB using Runtime.getRuntime
().totalMemory(). If the limit is 16MB, then why doesn't the total
memory grow to make room for the bitmap? Instead it throws an error.
I also don't understand that if I have 1.6MB of free memory according
to Runtime.getRuntime().freeMemory() why do I get an error saying "VM
won't let us allocate 614400 bytes"? Seems to me I have plenty
available memory.
My app is complete except for this problem, which goes away when I
reboot the phone so that my app is the only thing running. I'm using
an HTC Hero for device testing (Android 1.5).
At this point I'm thinking the only way around this is to somehow
avoid using BitmapFactory.
Anyone have any ideas on this or an explanation as to why VM won't
allocate 614KB when there's 1.6MB of free memory?
[Note that (as CommonsWare points out below) the whole approach in this answer only applies up to and including 2.3.x (Gingerbread). As of Honeycomb Bitmap data is allocated in the VM heap.]
Bitmap data is not allocated in the VM heap. There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library.
Unfortunately, while the definition of BitmapFactory.decode...() says that it returns null if the image data could not be decoded, the Skia implementation (or rather the JNI glue between the Java code and Skia) logs the message you’re seeing ("VM won't let us allocate xxxx bytes") and then throws an OutOfMemory exception with the misleading message "bitmap size exceeds VM budget".
The issue is not in the VM heap but is rather in the Native heap. The Natïve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage. But, given that BitmapFactory will not return, you need a way to figure out if the call is going to succeed before you make it.
There are routines to monitor the size of the Native heap (see the Debug class getNative methods). However, I have found that getNativeHeapFreeSize() and getNativeHeapSize() are not reliable. So in one of my applications that dynamically creates a large number of bitmaps I do the following.
The Native heap size varies by platform. So at startup, we check the maximum allowed VM heap size to determine the maximum allowed Native heap size. [The magic numbers were determined by testing on 2.1 and 2.2, and may be different on other API levels.]
long mMaxVmHeap = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
mMaxNativeHeap = 24*1024;
else
Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);
Then each time we need to call BitmapFactory we precede the call by a check of the form.
long sizeReqd = bitmapWidth * bitmapHeight * targetBpp / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
// Do not call BitmapFactory…
}
Note that the heapPad is a magic number to allow for the fact that a) the reporting of Native heap size is "soft" and b) we want to leave some space in the Native heap for other applications. We are running with a 3*1024*1024 (ie 3Mbytes) pad currently.
1.6 MB of memory seems like a lot but it could be the case that the memory is so badly fragmented that it can't allocate such big block of memory in one go (still this does sound very strange).
One common cause of OOM while using image resources is when one is decompressing JPG, PNG, GIF images with really high resolutions. You need to bear in mind that all these formats are pretty well compressed and take up very little space but once you load the images to the phone, the memory they're going to use is something like width * height * 4 bytes. Also, when decompression kicks in, a few other auxiliary data structures need to be loaded for the decoding step.
It seems like the issues given in Torid's answer have been resolved in the more recent versions of Android.
However, if you are using an image cache (a specialized one or even just a regular HashMap), it is pretty easy to get this error by creating a memory leak.
In my experience, if you inadvertently hold on to your Bitmap references and create a memory leak, OP's error (an referring to the BitmapFactory and native methods) is the one that will crash your app (up to ICS - 14 and +?)
To avoid this, make your you "let go" of your Bitmaps. This means using SoftReferences in the final tier of your cache, so that Bitmaps can get garbage collected out of it. This should work, but if you are still getting crashes, you can try to explicitly mark certain Bitmaps for collection by using bitmap.recycle(), just remember to never return a bitmap for use in your app if bitmap.isRecycled().
As an aside, LinkedHashMaps are a great tool for easily implementing pretty good cache structures, especially if you combine hard and soft references like in this example (starting line 308)... but using hard references is also how you can get yourself into memory leak situations if you mess up.
Although usually it doesnt make sense to catch an Error because usually they are thrown only by the vm but in this particular case the Error is thrown by the jni glue code thus it is very simple to handle cases where you could not load the image: just catch the OutOfMemoryError.
Although this is a fairly high level answer, the problem for me turned out to be using hardware acceleration on all of my views. Most of my views have custom Bitmap manipulation, which I figured to be the source of the large native heap size, but in fact when disabling hardware acceleration the native heap usage was cut down by a factor of 4.
It seems as though hardware acceleration will do all kinds of caching on your views, creating bitmaps of its own, and since all bitmaps share the native heap, the allocation size can grow pretty dramatically.