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.
Related
I recently created an android application, and after I completely redid my spinner dropdown menus using a custom adapter, some devices are crashing. It's very minimal, like 3 people have experienced crashes ever, but still it's something I want to address if possible because the apps minimum SDK version is as low as 16, so I imagine it's older devices that struggle the most. Attached is a pic of the crash report! This specific device has 2 GB of memory, you would think that would easily be enough to load a spinner with a bunch of low quality images right?
Thanks for the help in advance! This is the crash report: https://i.imgur.com/Wtm5pX9.jpg
The amount of memory the device has != the amount of memory you can use. The OS takes a lot. Other apps take a good amount. And even the memory you can use is fragmented into different pools. Generally "OutOfMemoryException" means out of Java heap memory (out of native memory would be a different crash, for example). Bitmap memory goes into different buckets on different OS versions, they've changed it a few times. So it could be a variety of reasons- you have too large an image that's using insane memory, your have leaks, you have sufficient memory on the device but the heap allocation spiked for some reason, your network layer isn't efficient (if you use Volley to download images its particularly stupid about that). There's not enough info here to actually give you a suggestion. I'd try to replicate it on a simulator with the RAM purposely capped at 1GB or smaller and see if you can reproduce it.
I am new in game development and want to develop a phone game. I am trying to make a complex animation in libgdx, which consists of nearly 300 images (655 × 1160 each, together in size of 42MB). Is there a limit in how big textureAtlas image can be or how many pictures it takes? Will there be a problem if I render 7 animations at once, while 2 of them are as complex as this one (activated skill), but others are just simple character movements?
The atlas can be as big as you have RAM for. The more RAM you have, the bigger atlases you can load.
On Android, you may have problems loading 42 MB big images as it's a lot of allocation at once. There have been multiple posts on Stack Overflow about just that problem. You just have to try loading it and see what happens. If the game crashes with an OutOfMemoryException, you have to make it smaller.
Because Android is a platform with a lot of variation in hardware (some devices have 512 MB total ram while others have 6 GB) there's no "hard limit" to what you can load in on a platform basis. It depends on what device and how much RAM is available (free RAM)Disclaimer: this answer is old and doesn't represent a realistic RAM distribution anymore. With certain phones having as much as 16 gigs of RAM and most flagships boasting at least 4 GB, 42 MB shouldn't (realistically) be a problem anymore. Android RAM allocation is still one of the weirdest systems in existence, and sufficiently large bitmaps may throw an exception separately. Bitmaps in this case do mean through the Bitmap class in "regular" android, and not directly OpenGL and/or LibGDX.
What the TextureAtlas actually contains (complex animations of 30 images vs 2 massive images) doesn't matter. As long as you load it (and manage to get it into memory without the game crashing) there's not going to be a problem rendering it. The framerate may drop though (depending on hardware and how much you actually render) but unless you render thousands of images at once it shouldn't be a problem
Adding android:largeHeap="true" in the manifest is also helpful.
I'm working on a graphical app for which I want to keep an array of bitmaps that are used for Undo operations. The bitmaps are quite large at around 9M each, so I realise I can only keep a few in memory at any given time.
I'd like some way of working out in advance how many I can have.
I've tried various ways of querying available memory, and am being careful to recycle bitmaps once they are not needed, but despite that the app seems to crash with EOutOfMemory.
I don't want to scale down the bitmap, or use RGB565. I just want a reasonably reliable way to figure out how many undo steps I can allow for.
Thanks
EDIT #1
I've continued to try various ways of determining available memory, including those linked to in the comments, but still am having problems.
The strange thing is that my old Samsung I9000 phone doesn't have too many problems creating and accessing lots of bitmaps each 9MB in size, but my newer Samsung Tab 3 dies allocating the 3rd one.
It should have plenty of memory available. I did read something about there being differences in where memory is allocated for bitmaps on Android 3 and above, but don't fully understand it. Could this be what is causing my Tab to die with EOutOfMemory?
EDIT #2
In desperation I decided to turn on largeHeap in the manifest. I know it's not recommended, but it has made the Tab 3 behave more predictably, and it possibly demonstrates something about the underlying problem.
This reminds me of a very common mistake , of putting the image files into the "res/drawable" folder.
Such a thing causes the bitmaps to take much more memory the higher the screen density is.
for example, for a 100x100 image, it would take only 100*100*4 = 40,000 bytes on an mdpi device, but it will take (2*100)*(2*100)*4 = 160,000 bytes on an xhdpi device (4 times more).
however, since the galaxy tab 3 doesn't seem to have a high density screen, i think that you get OOM because the heap size is small for holding all the bitmaps.
check out my post here for some memory and bitmaps tips.
It seems that getting available memory is a bit quirky in Android, but it turned out that my main problem was that on newer versions of Android the memory allocation for bitmaps has changed, and they now easily blow the limit of the heap.
Setting largeHeap in the manifest got me around that problem, but I'm still not sure it's ideal.
I am creating an Android app and I have yet to run out of memory until just now. The part that is confusing is it was not a way I would expect to run out of memory.
I simply added a new Linear Layout to a layout xml file. It had a background of an image that was 40kb large. The other images that were loaded were approximately 12kb with the background being much larger at 120kb. When this image was loaded I would receive a Fatal Signal 11 on runtime. If I removed the background from the layout it would work fine. I then changed the image to be much smaller, it was (2000x600) before so now it is 14kb. It now works fine. It is clear that the extra 26kb was causing a problem...
What kind of precautions should I take to ensure this doesn't happen again. Obviously I would like quality images and the 2000x600 might be excessive as it's an app for a phone but what guidelines should I follow?
Is there a memory usage cap that can be extended or that I should look to stay under? Also, if I loaded many images on a scrollview will this cause the app to crash? I have had many images loaded at once but have never had it crash before, I'm just confused as to the error being caused by very little memory usage. (In todays terms at least.)
Regards,
Jake
Images do not consume the same amount of memory in RAM as they do on disk. You can get the RAM usage by:
width*height*4 (for images with an alpha channel)
and
width*height*3 (for images without an alpha channel)
An Android app is allotted a definite amount of heap space in the RAM, which will always be the same on a particular device and ROM version on that device. The minimum is 16 MB, though these days most devices give you a more comfortable 32 MB or 64 MB. It really depends on the device.
You get Out of Memory Exceptions (OOMs) when your app exceeds this heap space. The simple fix is: Don't exceed this heap space.
The best way to get quality images in your app is to use the density buckets and provide different sizes from which Android can choose. LDPI screens cannot make use of high res images anyways, so you should only supply a low res on in LDPI. On the other hand, HDPI devices can make use of better resolution, and if you supply one Android can use it. It is almost a given that the higher the resolution of the device, the more heap space you will be allotted for the running of your app.
Another way to manage memory is to only load what you need, by implementing lazy loading. This is especially helpful with listviews and scrollviews and other adapter based View systems.
On Android 3.0 and above, you can request a bigger heap by using android:largeHeap="true". However, this should be avoided as the user will notice other apps of theirs being removed from memory whenever your app is launched, and it is not guaranteed that you will receive a larger heap in any case.
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.