Android deallocate Bitmaps going into "unknown" memory - android

I'm making a sketching app, mostly for fun. But i've run into a problem.
Within my app, the user can "flip" through sketches they've already drawn. These sketches are just 400x800 Bitmaps. But i'm constantly running into out of memory errors.
Sketches are saved to SD as soon as the user flips away from them, and they are then deallocated with...
if(bitmap != null)
{
bitmap.recycle();
bitmap = null;
}
So, only one or two Bitmaps should be loaded into memory at any given time, before they are saved and deallocated. And with a few debug prints, i've determined that this is true (max. two bitmaps in memory at the same time). My problem is that after 14 loads/deallocations, the program crashes with an OutOfMemoryError. Always 14, no matter how long i wait between "flips".
I've tried opening DDMS to see what the device's memory allocation is like. Strangely, every time an image is deallocated, memory goes to the "unknown" section, instead of the "free" section. This continues until there is only a sliver of "free" memory left, at which point the crash occurs (and the "unknown" memory goes to "free").
After a lot of googling, i've found a few resources which say that bitmaps should be deallocated as expected, and that Dalvik garbage collections always occur before an out of memory error. And a few miscellaneous pages that suggested that ALL memory used by a Bitmap is deallocated upon GC (native and JVM heap alike)
I should note, at this point, that i'm testing on two different phones. Motorola Droid X (stock image, Android 2.3.3), and Samsung Galaxy S3 (Cyanogenmod 9, Android 4.0.4). Both exhibit the exact same behavior. My android tools are up to date with all SDK versions, and i've checked a million times to make sure that nothing else in my code is doing any weird allocations (practically nothing is allocated after program startup).
So, how should i properly deallocate a Bitmap so that it is freed (not just "unknown"), and i can use the memory elsewhere?
As a sort of supplemental question, what is this "unknown" memory pool, and what is it for?
EDIT: Grammar and spelling, added some notes on platform.

Use large image (also 1mb near). You may be use androidmanifest in application tag set large heap true.and than use custom View not use imageview (because too complex and need more than other views)

Related

Proper memory management in OpenGL on Android devices

We have an app where we draw many things (mostly OpenStreetMap Tiles but also other images) with OpenGL ES 2.0. This works pretty good, but from time to time the app crashes without any information - this means: The app just closes, not a message that it is crashed and also there is no message in logcat.
From experiments we've figured out, that the app crashes after loading too many textures into OpenGL (glGenTextures/glBindTexture)
In an experiment on a Samsung galaxy S3 we are able to load up to 1800 textures with a size of 256x256 in RGB888 standard. After that our app crashes without any error logs. Shouldn't we receive an error(GL_OUT_OF_MEMORY) when we are constantly checking for OpenGL Errors (GLES20.glGetError) while loading textures?
Generally asked:
Is there a way to determine the maximum size available in gpu memory or at least, how do we get a warning as soon as we are running out of memory? (The problem is we do not know WHEN we should start deleting handles and we want to keep most of them as long as possible...)
Thanks in advance for any replies.
So after some research we found a solution.
But first of all:
These methods won't help and either won't these.
The methods mentioned in the first post will get you absolutely useless numbers which won't help you at all and may not even be correct (since my galaxy S3 got a much higher number than a nexus 7. But in our tests the nexus 7 could load the same amount or even more textures than the S3).
Then let's move on to our solution - which we are still not sure is the best, but it will work:
It look likes you can keep as many textures in OpenGL as you have free total RAM. Yes total RAM, not heap and not something other related to your app! It's really the amount of free RAM of your whole system, there are NO restrictions!
So once you figure that out you just read out the amount of free RAM you have:
ActivityManager actvityManager = (ActivityManager) mapView.getActivity().getSystemService( Activity.ACTIVITY_SERVICE );
ActivityManager.MemoryInfo mInfo = new ActivityManager.MemoryInfo ();
actvityManager.getMemoryInfo( mInfo );
Log.v("neom","mema " + mInfo.availMem/1024/1024);
Best place to do that is probably in your onSurfaceCreated method or somethign like that.
The amount of memory you get there is the total free RAM you currently have. Mostly this value is too low, in almost any case you can even allocate more memory than you get there (in our tests we could always allocate about 30%-50% more RAM than the availMem gave us until the app crashed (tested on Nexus 7 (+28%), Galaxy S3 (+36%), Transformer T1 (+57%), Nexus S (+57%))). Reason for this is: Android will free memory from unused stuff, background processes and so on, before it really runs out of memory.
So what does this mean?
You can (almost) safely keep as many "bytes-of-textures" in OpenGL as you have free RAM. For example: Lets say you have 375 MB free RAM and textures which each have a size of 256kb, you could keep about 1500 Textures in OpenGL before you start deleting some of them (with an LRU cache or something like that). You could even keep more, but I think you are pretty safe with this amount.
Sureley you could also get the availmem more frequently and just fit your cache size or what ever you are doing to this new amount of memory - but I think it suffices if you do it once in onSurfaceCreated - especially since this will be read out again if you switch to another app and back.

Out of memory error: vast bitmap

My activity has listview and (apart from all other stuff) loads images from web and displays them in listview. I have access to 5 android devices: 2 HTC desire, LG P-350, one more phone and a tablet. Normally, everything works fine, but being launched on one of HTC desire, app tends to crash with NullPointerException, which is due to out of memory error (I guess so), this is the output:
05-03 14:41:23.818: E/dalvikvm(843): Out of memory: Heap Size=7367KB, Allocated=4991KB, Bitmap Size=16979KB
Later, logcat outputs stack trace of nullpointerexception where one of my static variables suddenly becomes null (the variable is initialized in app's root activity, is used across the app and for sure is not nulled in code). I suppose, it is nulled by system due to lack of memory.
As far as I undesrstand, system tries to allocate bitmap as large as 17mb - I'm sure loaded images cant be that big. They are 100*70 jpegs and any of them weighs far less than 1mb.
Another thing I dont understand is why I get this error only on one device - other devices work fine.
To my mind, this looks very strange and I can find no clue, I need advice.
The reason is simple: the memory is not holding your JPG data per say, but rather its decompressed equivalent, which, needless to say, takes a lot more RAM space than the source files... Note that this 17 mb limit is for all your loaded bitmaps at once, not necessarily a single one.
I had to fight with similar problems in one of my programs (a custom Tile loader for a Mapquest Android API MapView object), and I ended up having to call the recycle() method of my bitmaps whenever possible, as well as manually oblige the system to garbage collect at strategic locations using System.gc()...
Sorry to not be the bearer of the best news...
You might solve your problems using the same strategy as I did: I essentially cache the loaded bitmaps in hard storage such as my external SD card, and reload them on the fly when needed, instead of attempting to hold everything in RAM.

Seemingly inconsistent OutOfMemory crashes on different Android devices

I am making a game which has to load all bitmaps at start because in the in-built game editor the user can put any of the sprites into the level. Also levels use various sprites without any system which would allow to load groups of sprites dynamically for each level.
After a while the are now already 250+ png images in the game with total size of 3.5MB.
The game loads most sprites (about 200) using BitmapFactory.decodeStream without any options set, and also there are about 50 other which are referenced in xml layouts of activities.
When I test on various devices, the game sometimes runs out of memory, but i can't find a pattern and even decide by HOW MUCH i have to, e.g. reduce the size of images or their number.
The phone on which i developed, HTC desire with Android 2.2 24MB VM heap size never runs OOM.
Dell Streak with Android 2.2 and 40MB VM heap size never runs OOM, too.
Motorola Milestone with Android 2.1 and 24MB VM heap size successfully loads all sprites but chokes on the few last images used in ImageView's when starting one of the activities (start menu). If I comment a few of such ImageViews out, it loads ok, but may choke on one of the other activities later. It's also not stable, probably because fragmentation happens differently in different launches.
HTC hero with 2.2 of my buddy (dunno the heap size, is it 16MB?) crashes as well.
What's most confusing is that Motorola has 24MB, the same as HTC desire. Is 2.1 implementing memory management less efficiently? (e.g. leads to more fragmentation?) Or is memory management worse by all Motorola phones? Then why does HTC hero with 2.2 crash? What's bigger in HTC desire than HTC hero?
Looks like OOM happens on older phones, but that's the only thing I've figured out so far.
If OOM only happens on older phones which are, say, 5% of the market, I can just exclude 2.1 or a more specific list from the supported devices by just gathering crash reports and excluding all that crashed from the list of supported. Otherwise I'd now need to scale down all my images by some constant factor (e.g. 1.6), which would mean resizing all the 45 levels which took days and days of designing and testing, repositioning GUI elements etc. Even after that, I'd still not be sure, on which devices the reduction of total size of bitmaps by a factor of e.g. 2 is enough to avoid OOMs.
Any advice on this?
Should I set any specific options for BitmapFactory? btw, most images have transparent bg pixels which, as far as i understand, doesn't allow getting them in 565 format.
I spent 2 days browsing here and in other places but am still at a loss.
Thank you.
I've had to deal with a simpler version of your problem - we had 3 2Mpix layers on top of each other, which, unsurprisingly, sometimes caused OOM.
In my case, instead of using 3 ImageViews on top of each other, keeping all 6 MPix in memory at all times, I manually blended the layers, thus keeping at most 4 Mpix in memory at any one time (and only 2 MPix at "rest" - the layers changed in our application).
This is a classic time-space trade-off - sacrifice efficiency (time) to gain memory (space). It was somewhat slow because you had to recycle() each Bitmap after you were done with it to ensure the memory was freed.
As for the inconsistent OOMs, it probably has to do with garbage collection. You can assume that the garbage collector is non-deterministic and thus any memory pressure becomes non-deterministic as well (depending on when the GC last kicked in).
The short summary is, you have to be very, very careful with your memory usage and there's no way around it. *
* In theory, you could allocate memory outside the Dalvik heap by using the NDK and allocating straight from the OS. It's a huge hack and the bridge between Dalvik and your allocator will be rather ugly.
First you need to find what exactly is using all of your memory. Check this out. Maybe you could be recycling bitmaps, for instance.

Is there a way to compact memory in android to lower the high water mark?

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.

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