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.
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.
From time to time, especially when implementing new functionalities in my app, I use DDMS + HPROF to analyze memory and heap use. As the App doesn't seem to have any performance-ANR issues and everything works smoothly, initially I didn't care about it - but now, as I see it's approximately always the same size, I'm wondering what the damn can it be.
Everytime I run a HPROF, I check the Leak suspects tab. There's always an android.graphics.Bitmap instance that takes approximately 25% of all the used heap.
I wanted to know a bit further what's that about, so I opened the dominator tree, and saw this:
So esentially there's a huge byte[] instance that is retaining a lot of heap, and never gets released. According to this, I copied the value of that byte[], dumped it into a .data file, opened with Gimp, and there's the result:
So basically, it looks like the "alpha(0)" part of a PNG image. Having in consideration the following facts:
All my image files are <8K in size
Just some of them are PNG - the remaining I was able to convert to JPG, I did
No matters if I add further images, the size of that byte[] has been always aproximately the same from the beggining of the app (4 months ago)
To debug it, I tried to remove any image file from the drawable and drawable-xxx folders and run the app without any drawable resources, and the byte[] was still there
I removed almost all layouts and let just the basic funcionality, and same result
In the Dominator tree, the root class is android.graphics.Bitmap
Anyone knows what it this byte[] and if I should do anything to free it up?
Any help appreciated!
Just to clarify a few things:
Images/drawables in your app are used in memory in the form of android.graphics.Bitmap.
As of Android 3.0 (API level 11), the pixel data is stored on the Dalvik heap along with the associated bitmap. (Managing Bitmap Memory)
"huge byte[]" is probably an overstatement as it is just over 1MB of heap space.
The Leak Suspects report can be helpful, but in this case it isn't telling you much considering that its largest suspect is just over 1MB of memory. Modern devices are offering 64MB+ heaps.
Let's do the math for the memory requirements for this bitmap. This bitmap is taking up 1,127,584 bytes on the heap. If we were to assume this bitmap is configured using ARGB_8888, each pixel is using 4 bytes, which means your image contains 281,896 pixels (or roughly 530x530). Does this sound unreasonable for what you are doing?
Also, consider the way Android scales across the different "buckets" for drawables: mdpi, hdpi, xhdpi, etc. Let's say you have a 200x200 image in the mdpi bucket and you are opening the app on a xhdpi device. This image will be scaled to be twice as large and will have a on-device resolution of 400x400. So while the 200x200 image may not take much heap space (200 x 200 x 4 = 160 kb), the 400x400 image will require a relatively larger amount (4x) of heap space (400 x 400 x 4 = 640 kb). For more information on this, see Supporting Multiple Screens.
A nice tool for quickly computing differences with the image buckets: Android DPI Calculator
You said you removed some of your drawables, but what is left? Have you considered drawables that may be coming from external libraries?
To answer your final question: Anyone knows what it this byte[] and if I should do anything to free it up?
I would say: This small amount of memory on your heap is nothing to be worried about. If it is bothering you, keep an eye on it and make sure it isn't growing beyond what seems practical. If you still suspect a memory leak, navigate between screens and watch to see if the heap continues to grow. Assuming you are not caching bitmaps, the heap should maintain a consistent/predictable size when navigating back and forth between two screens
As a side note, DDMS makes it very easy to monitor heap size on the fly. No need for HPROF dumps until you are ready to dive in. Have a look at Using DDMS. Take special note of the "Cause GC" button as it will be needed to trigger the update of the initial heap size.
-- UPDATE --
To further answer this, one unsupported suspicion I have is that some of the app's assets (system assets/textures?) are loaded into your app's memory space. Have a look at slide 64 here: What's new in Android 4.4.
Android 4.4 now generates a single texture containing all the framework assets, shared by all processes. This saves a bit of memory in every process but it also helps batching and merging drawing operations to optimize applications automatically.
This seems to imply that memory is used for system bitmaps/drawables in each app running a version prior to 4.4. If this is the case, I would question if this 1MB is that space. I wonder if you could run your app on a 4.4 device/emulator and see if the same memory is used.
As another test, have you tried inspecting memory usage on a barebones app (all drawables removed, etc)?
Guess what, another Android-Bitmap-OOM question!
Background
Whilst stress testing our application it has been noted that it is possible to max-out the app's process memory allocation after sustained, heavy usage (monkey runner like) with OutOfMemory exceptions being recorded within the ensuing stacktrace. The app downloads images (around 3 at a time) when a page under a ViewPager is selected. There can be 280+ images available for download when the length and breath of the app is exercised. The application uses Picasso by Square for it's image downloading abstraction. Notably, at no point in our application's code are we manipulating Bitmaps directly...we trust that the very talented Square Inc. employees are doing it better than we can.
Here is a picture
The below plot shows the heap allocations over time recorded under the dalvikvm-heap log message. The red dots indicates a user bringing a fresh set of articles into the application in order to bolster the amount of work outstanding and stress the app...
DALVIKVM heap allocations http://snag.gy/FgsiN.jpg
Figure 1: Nexus One heap allocations; OOMs occur at 80MB+
Investigation to-date
Against a Nexus S, Nexus 4, Wildfire, HTC Incredible and a myriad of further test devices, anecdotal testing has shown the memory management to be sufficient with the DVM GC 'keeping up' with the heavy lifting work being completed by the app. However, on high end devices such as the Galaxy S II, III, IV and HTC One the OOM are prevalent. In fact given enough work to do, I would imagine all of our devices would eventually exhibit the failure.
The question
There is clearly a relationship between screen density (our requested image sizes are based off the size of the ImageView), the process memory allocation and the number of images at a given size that would result in the app exceeding it's heap limits. I am about to embark on quantifying this relationship but would like the SO community to cast their eyes over this problem and (a) agree or disagree that the relationship is worth making and (b) provide literature indicating how best to draw up this relationship.
It is important to note that if we destroy the image quality our OOM all disappear but alas the UX is poorer which is why we are wanting to be dicing with the most effective use of the available heap.
Side note: Here is the portion of code responsible for loading these images into the views that have been laid out;
picassoInstance.load(entry.getKey())
.resize(imageView.getMeasuredWidth(),
imageView.getMeasuredHeight())
.centerCrop()
.into(imageView);
The 'dashing of image quality' mentioned above is simply dividing the imageView.getMeasured... by a number like '4'.
First you need to manage the memories allocation ,its a big issue in android as bitmaps takes lots of memories ,for that memory allocation can be reduce by following ways
put all those images which are huge in size to assets folder instead of putting them in drawabable folder . because drawable resources takes memory for caching them .if you load from asset folder the image will not cache .and will takes less memory .
study Lrucache which use for efficient memory management .
put resources in tiny formats for that check TinyPNG
if your images are too large in resolution , then try to use SVG files for images and load SVG file instead of image . check this SVG FOR ANDROID
finally i am not very good in English hope it may helps you.
This post is a little old but I also had this issue recently. Maybe this will help someone else.
General Overview of this massive thread/What helped me.
-Make sure you are using a Singleton Instance of Picasso
-Use fit()
-For large Images or many Images or when used in a FragmentPager/StatePager you should probably use skipmemorycache() and/or largeHeap declaration
Read the thread for more tips. At the time this question was posted nobody had posted this issue on picassos github.
https://github.com/square/picasso/issues/305
I got primarily 2 issues.
In fact: I made an app which uses neat ui elements. Because of their detail they have to have a fitting resolution for every screen so i do big pictures in drawable-xhdpi, smaller ones in drawable-hdpi, etc...
The problem is: Using such high resolution UI elements slows down the whole app. It's not yet the worst-case but I am really stressed with the fact that I cannot make a "perfect" app which is beautiful and does not lag.
The second issue is: The app really becomes big when considering tablets also (xhdpi images are bigger). Out of compatibility reasons I dont want to make an extra tablet app...
Somebody got some information for high-res problems?
When using large images on android i found the available heap can be a constraint.
As the lower-res images are scaled up the simple drawing-performance itself should not be any problem.
But every image you need to keep in the heap is reducing available memory for everything else you can do without getting onLowMemory callbacks.
There is an option within the Android Manifest to allow more heap on newer devices
android:largeHeap="true"
This will not give you any more heap on most devices but if you try it on a modern device you can see if it makes a difference to decide if memory might be a problem.
It also can help to enable hardware acceleration.
<application android:hardwareAccelerated="true" ...>
Another option is to improve performance on images which you need often.
The BitmapFactory.Options have an inPurgeable which control if the Bitmap is freed when memory is low.
But ... even if memory is a problem it is morelikely that the views have a too complicated layout.
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.