OutOfMemory when allocating int array < free memory - android

In my android app I'm allocating a large int array. This sometimes gives me an OutOfMemory error, when I think there should be sufficient memory. This is an example of what I get:
// always this value
ActivityManager.some_instance.getMemoryClass() = 128 Mb
// always this value
Runtime.getRuntime().maxMemory()/(1024*1024) = 128 Mb
// example value
Runtime.getRuntime().freeMemory()/(1024*1024) = 81 Mb
// example value, can also be bigger than freeMemory()
arrayLengthToAllocate*4/(1024*1024) = 47 Mb
To be clear, I get the OutOfMemory error for situations where the last value is larger or smaller than freeMemory().
Why do I get the error? Is the heap size not increased when the allocation is performed? The memory use of the app just before trying to allocate is about 8 Mb, so that cannot be the problem.
PS. Other approaches than using an int[] of what this app is doing are not possible.

Generally speaking most devices have a cap on the amount of memory you can use. I know that originally this was set at 16MB per application. I think this has increased since, but it is still something you should be taking into account.
You also have to bare in mind that your applications footprint can fluctuate and that the Android Operating System has to give memory to other applications as well which are competing for resources. It may be that Android is identifying your application as being a resource hog and is cutting back it's resources.
Storing 47MB of integers in memory cannot be a very good use of an application's memory, is there no easier approach? I fail to see why you would need so many integers to be so readily available. Surely adding the integers to a SQLite database, reading them in as required, thus moving the bulk of the memory requirements away from RAM and onto physical memory would be a better approach?
When you are hitting OutOfMemory errors it is normally an indication that you are being a bit too greedy and the best way of limiting them is to scale back what you are doing, find a different approach.

The memory amount you get is the system memory size. Each application has a limited subset of that, which depends on a few system variables. In general it's something in the order of 16 - 32 MB, sometimes a bit bigger for tablets.

I figured out that the problem is heap fragmentation. Defragmentation has limits such that there are simply not enough free consecutive bytes in the heap to fit the single large array. I restructered the data into a two-dimensional array which solves the problem as each row has its own location in the heap. Downside is that retrieving the data from the array is slightly slower.

Related

Trying to reduce app memory, what values should I log?

My app often runs out of memory. I'm working on reducing memory usage but I would like to log how much memory on average my users use, have available, etc, to see if the changes I make are having any effect on the real world.
I found these values I could capture:
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
long maxMemory = Runtime.getRuntime().maxMemory();
But I'm not certain those are the right ones. Is that freeMemory value one that would tell me if I'm likely to get an OutOfMemoryError? If not, which values should I log?
Is that freeMemory value one that would tell me if I'm likely to get an OutOfMemoryError?
Probably not.
An OutOfMemoryError will occur when you try to make an allocation and there is no free block big enough to fulfill that request, even after an emergency garbage-collection run. On Android 7.1 and older, due to the nature of the garbage collector, this can occur because your heap gets fragmented. freeMemory() indicates the sum of all free blocks, not the size of the largest free block.
So, you need to look at the OutOfMemoryError crashes that you are getting. Typically, in Android, those will come from large allocations, such as loading a Bitmap. If that is the source of your crashes, then freeMemory() will be of little use, and there is nothing that you can log.
If, on the other hand, you are getting crashes for fairly small allocations (say, 128 bytes and lower), then most likely you really are exhausting your heap. In this case, freeMemory() may be useful to log.

Android: why is memory usage for one application drasmatically larger than allowable memory for one process

As I researched, Android allocates limit memory for each process, maybe range from 16MB to 24MB for each one. Here is reference
Nevertheless when I view memory usage for one application in setting, I often see one normal application costs hundred megabytes for memory (on one process). There is a conflict here that I cannot understand.
Thanks :)
NDK code can use more system RAM than can a single Dalvik/ART process. Also, the app might be using more than one process, or it might be using android:largeHeap to request an above-normal heap size.

How much hashMap operation can be called 'safe' at a time

I am developing an application in which I have a database with 5000 rows with 4 columns.
problem_id (int)
problem_no (string)
problem_title (string)
dacu (int)
I need to frequently query single items in a large scale like 1000 query to fetch problem_no based on problem_id or sometimes only one item.
So I decided to query all the database rows and map them in a hashMap at runtime. I know hashmap insertion/query operation will take only O(1) or sometimes little more, so I only need 5000 operations I think. But how much space hashMap will take in this case? Would android dalvik be able to allocate them without any trouble?
How much space will hashmap take?
It's an implementation detail that can vary between versions, devices, etc. As long as we understand that and look for an estimation only, you can actually measure it very easily. Android SDK includes a powerful suite of memory analysis tools. Check out Eclipse MAT (the best one in my eyes). You can take a heap snapshot when your hashmap is fully loaded, then use MAT to see how many bytes it takes. Make sure you sum up both the hash itself, the keys and the values (if I remember correctly MAT can do the math for you too (it can handle the core collections very well).
Will dalvik be able to allocate?
For the sake of discussion let's say your hashmap takes 1MB of memory. To get a feeling if that's much, we need to understand the constraints of the system we live in. Dalvik limits the max size of your heap. The limitation varies per device. The minimum on very old devices is 16MB. Devices like Samsung Galaxy 2 have about 32MB-48MB and new devices like Galaxy 3 and 4 have more than 100MB.
The biggest memory hog in apps is usually bitmaps. Since every pixel can take as much as 4 bytes, a full screen bitmap can easily eat up a few MB of memory.
With this in mind, a toll of 1MB doesn't sound bad. It's comparable to a using a nice background image :) if your overall memory usage is low, you can distribute it as you see fit. The memory analysis tools (MAT or DDMS) let you know exactly how much memory your app is currently using, so you can easily estimate how much your total consumption will be.
Other thoughts:
Caching things in memory to improve performance is usually a good idea. So your approach is a good one in my eyes (as long as you understand the memory implications).
Since your memory hashmap is an optimization only, you can be extra careful and only do it when you have memory to spare. You can easily measure the amount of available heap (the is API for that) and make your decision accordingly. You can listen to low memory notification events (google about those). And you can even catch OutOfMemoryError exceptions of failed allocations and change your memory strategy in runtime.
You are playing in a field where exact measurements are difficult. Be sure to QA on several devices and several versions of Android. To simulate low memory settings, try to use the oldest devices you can find.
I personally think that you will have absolutely no issues handling what you want in memory. Especially if those things are just primitives (no bitmaps).
I have used queries for up to 10k rows for caching in memory and had absolutely no issues in terms of memory for them.
The issue might happen when you need to process everything. Like how fast is it to get to a specific item, get all realated items etc..
One issue i have come accross was UI related. I tried to just fill an adapter and show it in a list with all 10k of rows which took about 7 seconds to complete. It was long time ago and i don't recall why exactly that happened, but what i am saying is that i would pay more attention to keep processing outside of the UI thread and manage that as much as possible rather than memory in your case.

Why does Android allocate more memory than needed when loading images

Folks, I don't think that this is a duplicate and is NOT one of those how do I avoid OOMs questions. This is a genuine quest for knowledge so hold off on those down votes please...
Imagine I have a JPEG of 500x500 pixels. I load it as ARGB_8888 which is as "bad as it gets".
I would expect Android to allocate 500x500x4 bytes = a little under 1MB however, look at a heap dump and you will see that Android allocates significantly more, often factors of 5-10 times greater.
You frequently see questions on here about OOMS where the stack trace shows a heap request of say 15MB and it is Always much larger than is required simply to hold the bytes of the image. The OP usually catches some downvotes then is bombarded with stock answers and comments about using less memory (thanks Romain!) and in scaling. I think there is more than meets the eye here.
Anybody know why this is?
If there is no apparent answer, I will put together an SSCCE if it helps.
PS. I assume that JPEG vs PNG etc is irrelevant since we're talking about the memory usage of the backing bitmap which is simply x times y times BPP - or am I being slow?
It used to be quite a common trick with memory management to grab a pool or block of memory which is parceled out into smaller requests. When I worked with embedded systems it was a common practice to maintain pools of memory of different sizes, and we just allocated a block larger than the amount requested from a pool . It is a convenient way of preventing too much memory fragmentation. Maybe this is happening here.

BitmapFactory OOM driving me nuts

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.

Categories

Resources