Many of the android Developers are get into this night mare OUT OF MEMORY ERROR or Bitmap Exceeds VM Budget
This situation will arise due to following reasons
1) When you are dealing with large Bitmaps or Images
2) When you are trying to rotate the device continuously
3) When you are using unnecessary Object and increasing the heap memory
Among three scenarios the first two are culprits third one we can catch it by using some memory profiler tools and able to fix it.
How we can get rid of OOME?First two scenarios are happening quite often
It is not a mistake from Developer it is bug from Android OS code because the Dalvik Virtual Memory limits the amount of memory that any Android application can use to either 16 or 24MB. And there is no way to change this allocation pro grammatically. Only way is to alter the OS Code and recompile But that is far from our hands as millions of users using the application we cant tell them to recompile the OS to run this application . But the Vendors like Samsung,Motorola or those who are collaborating with Goggle can make this happen.
Iphone developers are safe in this area as they have the leverage to release the memory after use since Objective C is providing
Ok Now lets move to the first two points in Detail and the solution which we can do from our side
1)
Dealing with large Bitmaps
Now 16MB is enough to run even a complicated game but even though the VM runs out of memory this is because the OS is not releasing the bitmaps objects properly once the program is through with them. The only way the OS will release the memory when the application ends,and even it takes some time So lets see what are the solution to fix this
a)
Reducing the Size of Bitmap
This Solution involves scaling the bitmap and reducing the amount of memory usage but this will not solve the problem, it simply minimises its impact. This solution will work out if you are using very few bitmaps or images
b)
Bitmap.recycle()
This is good practice of programming when ever you are trying to deal with the bitmaps call Bitmap.recycle() this will immediately mark a bitmap for garbage collection if you are using collection of bitmaps don't forgot to call individually. But as this will also not giving a concrete solution as Java Garbage Collector is a low priority thread we don't know when it will collect the object. But its a good practice which will solve to an extend
c)
Utilize the Data Structures in Java
Suppose you are working with some multiple images make sure all bitmaps are in a Data structure so that all bitmaps are properly managed. Because sometimes even one Bitmap is enough to create the issue so whether it is a activity or back end Java class if you are dealing with multiple images there should be a function like this freeBitmapsUsed() and also make sure to override the finalize() method so that we can make sure that it is properly collected by the JVM
// call in order to free memory of Bitmap objects
public void freeBitmapsUsed() {
//traverse the data structure
for(Bitmap image: mImages) {
// also, it's better to check whether it is recycled or not
if(image != null && !image.isRecycled()) {
image.recycle();
image = null; // null reference
}
}
}
// the destructor should also handle the memory in case it still holds memory
protected void finalize() {
freeBitmaps();
}
Lets move to the Second Scenario
2)
When you are trying to rotate the device continuously
These is a serious OOME issue which almost all the developers faced a fast switching between the screen modes portrait/landscape. Why it is happening?
Its impact will be very high when that UI is constructed through code, or if that UI contains some large bitmaps, or it contains some complex canvas drawings.
These is a pure memory leak issue which we need to blame ourselves. Keeping in mind that you are Java developer not in a program where you have the span to deal with memory a lot
First suggestion you have to see requirement if that UI can be developed through xml then go for it that is the best practise because keeping different xmls in portrait and Landscape folders its a good android approach because OS itself will take care lot of de allocation no need to worry too much but there are certain circumstances where we need to create the UI through code or even we need to draw through canvas in that case we need to be little bit care. Whenever fast switching between the modes crash will happen.
It is happening mainly because you are referencing you context ie, your Screen Instance to a long time
That is when ever you are switching the modes from portrait to landscape and vice versa android is recreating your views it is taking the portrait layouts and resoures everything and literally recreating the view
So keeping this in mind Whenever a rotation happens we need to make sure there should not be any obstacle in our activity/UI which prevents the garbage collector from collecting that pointer/Screen Instance/ActvityContext
ie, You should not hold any static reference to Activity context. Playing with activity context needs to be careful at your risk a simple example
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
Its a pure memory leak code ,here you can see that Drawable is statically referenced. But you can see there is no direct reference to activity context. Still when you rotate the phone you can see a memory leak
that is your drawable
sBackground points to
label which in turns created from the context
new TextView(this); so when ever rotating the device by default GC_CONCURRENT will call which try to deallocate the context but it cant deallocate this activity instance as one of its child ie,
sBackground is statically referenced so what happens when you are continuously switching the mode to quite a long time you will get crash OOM by showing exceeds VM memory budget, ie, 16 or 24MB because your heap memory is increased on every switching
So make sure your context ie, a pointer which holds the whole complex UI component in fact this screen pointer in android which consumes a lot of your memory so play in safe mode with your activity context don't make any static reference directly or indirectly to this pointer which leaks your memory
You can see here let take right side as portrait and left side as Landscape the root one is our Activity Context/Screen Pointer . Your View is created by Lot of View Groups and Child Group if any of your View Group Components or View Child's is statically referenced then you are trapped. The moment when you switch it portrait mode OS recreates the whole View with revised layouts and images then it will try to reallocate the portrait context in that moment if you are making any hinderance through code you will encounter with the issue
Here another thing you need to notice the root pointer will be the summation of all spaces allocated by the View Components and View Child's then you can imagine its importance these statistics you can be clearly identified when you load a binary .hprof file to MAT tool in eclipse which i specified as last of this article
I already specified below an example log cat when ever you are on a leaking code when you make orientation in screen you can see in logcat the leaked memory status you can see your heap free statistics decreasing for more info you can see below log cat explanations
Thanks to Google as i had noticed from Honey Comb onwards they are collecting the bitmap object in right manner as they move this task from GC_EXTERNAL_ALLOC to GC_CONCURRENT you can see that in log cat so there will not be much out of memory issue due to bitmap from Honey Comb onwards but still the second matter about Context leaking on Screen Orientation is there so we need to take care that
Best way to analyze this memory leaks are put a tight monitoring on the logcats coming you can see that just put adb logcatt filter by giving the process id in command prompt
eg: D/dalvikvm(9050): GC_CONCURRENT freed 2049k, 65% free 3751K/9991k, external 4703k/5261k, paused 2ms+2ms
Just anlayze the message
First one is reason for Garbage Collection there are 5 reasons when a GC calls
GC_CONCURRENT :
It is a collection which will be kicked by the OS itself when they seems that
the heap is touching the VM budget (16/24mb) and it is a safe collection now after honeycomb they moved bitmaps into this category which is a good sign for developers they will take care its deallocation but keep in mind don't make any static reference to bitmaps allow them to deallocate properly without any hindrance
GC_FOR_MALLOC:
This is basically the OS cant able to finish the Concurrent garbage collection in time so that your application needs some memory in that case OS calls GC_FOR_MALLOC which will deallocate some memory from outside your application and make sure it makes the room for you
GC_EXTERNAL_ALLOC:
which will not be seen from Honeycomb because in older version they were used this for pixel allocation in Bitmaps which is a problem for developers but now they moved this to GC_CONCURRENT from honeycomb onwards
GC_HPROG_DUMP_HEAP:
Which you will see when you trying to dump the heap memory binary data to a .hprof file which is useful i will discuss in last of this article
GC_EXPLICIT:
That is developer when explicitly calls to collect that is our System.gc();
other message comments are
freed 2049k: that is you can see amount of memory freed from this collection you can see here 2049k
65% free 3751K/9991k: that is our heap statistics 65% are free now using 3751 out of 9991k
external 4703k/5261k: that is as i said the memory which they externally allocated for storing the pixel buffers in below Honeycomb version
paused 2ms+2ms: the time taken to GC to complete its operation if your heap is small it will take less time other wise long time.
So monitoring the log is a basic way but it is difficult and tedious so what you can do is you have the option in Eclipse tool where you can dump the heap memory and save in your local machine in .hprof format then Download the Eclipse Memory Analyzer (MAT) from
http://eclipse.org/mat and load the .hprof file which you saved. There goes you can see the statistics and you can find out the memory leaks everything. And also be handy with ActivityManager.getMemoryClass() which helps at the time of Development.
Thanks
Roger Gasper