Android Application Performance — Step 3: Memory
I’m moving on with the memory. You can reach my previous posts about rendering and compute.
At some kind of programming languages close to hardware as C, C++, Fortran, developers usually do memory management on by own. When allocating the memory and process are completed, developer are responsible for de-allocation. In that situation, because of developer’s being responsible from whole memory management, how truly it is implemented is related with developer’s knowledge and capability. To be able to get out of this chaos, managed memory languages are invented.
This languages track the memory allocations and if they release that application does not use by itself, they release the memory. The process is completed without developer’s any intervention. This is called as garbage collection.
The main principles of Garbage Collection:
- Find the data object that won’t be reached in future. (The memory won’t be used by code anymore)
- Release the objects that are used by these objects.
On the other hand, garbage collection could be dangerous, too. If there are 20,000 allocation in our code, which ones won’t be needed anymore? Or, when we execute garbage collection event to gain the unused memory back?
In Android runtime, memory heaps are segmented to spaces according to allocation’s type and how the GC events will be organized in best by system in future. When a new object is allocated, related with which android runtime is used and taking care of these characteristics, it is located in best way.
The important point is here, each space has a specific size. When the objects are allocated, the common size is controlled and when is started to fill, system needs to execute the GC event for future allocations. GC events work different according to which Android runtime is used. For example, at Dalvik, the whole code will stop until process is completed. The problematic situation starts here with GC’s taking to much time or tons of GC events happened at the same time, it means it will eat your frame time.
Although GC events are handled on by own, still some performance problems can occur. The first reason of this is sharing so many time to GC events and less time to other works. In that situation, other works will be done at the remaining time from GC events and it can cause missing the 16 ms frame window. Secondly, some parts of our code can cause GC’s being triggered frequently or taking to much time. For example, if we define so many objects unnecessarily in a loop that will continue for a long time, memory fills its heap with so many objects and GC is called many times.
Although we use managed memory environment, memory leaks still can exist. At this point, Memory Monitor which is on Android SDK can help us.
Memory Monitor
Memory Monitor is a tool that showing how your application uses the memory.
- Shows used and usable memory on graph and garbage collection events on time.
- If an application works always slow, it can be tested easily usage of garbage collection events so much.
- If an application crashes because of not having enough memory, it can be tested easily.
After your application is executed on device or monitor, you can start Memory Monitor following Tools->Android->Memory Monitor steps or under the Android tab.
When Memory Monitor starts to track your application, a stack that is tracking memory according to time shows up. You can see a sample of it under the pic. below.
Dark Blue: The memory being used by your application for that time.
Light Blue: Usable memory, not have been allocated yet.
When the time passed, graph updates itself and shows the changes on memory usage.
When your application allocate memory and de-allocate it, the graph changes. If you see a sharp decline on allocated memory, it means GC event executed. As same, if you see a sharp rise, it means memory was allocated because of your application’s having needed more memory. If it would not have been allocated, it would have probably crashed.
We can push the GC event to work as clicking the button as you see on pic. below.
The pic. above shows a healthy garbage collection execution sample. Memory allocations and de-allocations are done at some intervals. On the other hand, the pic. below shows a problematic output. As all we see, so many allocations were done in a short time period and then they were released immediately. Actually, it means so many GC events. I mean, it has been shared so much time to GC events and much less time to rendering and streaming.
Ok, how can we find out the memory leaks? At this point, another tool which is on Android SDK helps us, Heap Viewer.
Heap Viewer:
Heap Viewer is a tool that reports what kind of objects your application allocated and how much and which size of heap it uses.
It can be used on Android 5.0+.
You can reach the tool following Tools->Android->Android Device Monitor steps.
To be able to save a heap dump, we can click the Dump Java Heap button which is marked on the pic. above. A heap snapshot with name of Snapshot-yyyy.mm.dd-hh.mm.ss.hprof will show up under the Captures tab.
Heap updates exist after every GC event. We can trigger a garbage collection event as clicking Initiate GC icon.
We can open heap viewer by double-clicking heap snapshot file and see the immediate allocations in detail. We can click one of the data types to be able to get more detailed information. For example, when byte array(object[],int[],float[]) is clicked on the sample below, an histogram showing number of allocations and at the same time a specific memory size for this data type are shown up. For each data type, number of it, how much size it take and some that kind of information is shown cross of it which is very useful.
If we are going on talking about memory leaks, they are very sneaky. =) They are so slow and hide perfectly that sometimes to be able to understand that we have a memory leak can take days even weeks. Actually, when our users starts to complain about the mysterious slowness on our app., we can realise that we have memory leaks, in real. To get rid of this, a little patience(:)) and using right tools are very important.
Actually, what to do is simple. To understand whether there is a memory leak or not on our application, we can examine it as using Memory Viewer. If we see any problematic situation, then this time examine it with Heap Viewer more detailed.
Best Practises:
- Look for the objects’ life cycle on your code and clean the unnecessary references.
private void init() {
ListenerCollector collector = new ListenerCollector();
collector.setListener(this, mListener);
}
For example, at the code above, whole listeners for activity are stored in the init method of custom view. But, if we don’t clean the listeners on onDestroy then a slow leak will be created.
For example, if our activity is recreated for the reason of orientation change, a listener related with the view is created, too. But, when the activity is destroyed, this listener is not released and this means a memory leak is created by this listener which will never been released by garbage collector. We should be sure about whole listeners being cleaned onStop() of the activity.
Another useful tool is Allocation Tracker.
Allocation Tracker:
Allocation Tracker saves the application’s memory allocations and lists the allocated objects for profiling cycle, size and allocating codes by stack calls.
What is it useful for?
- Where was the similar object types allocated, de-allocated for a short time period?
- Finding the code parts that causes inefficient memory usage
Before using Allocation Tracker, we should profile our code using Memory Monitor Tool. After that, if we see lots of garbage collection events in a short time period, we can define the object types that can cause this situation and finally we can find the exact location of it using Allocation Tracker.
Allocation Tracker saves every memory allocation as long as application’s profiling cycle that moving on execution. We can stop the logging of Allocation Tracker as we started.
We can take log as following the steps which is on the pic. above.
After a few seconds, a pane is opened with our saved data. Allocations are logged to device, data file is transfered to computer, information is parsed and shown.
Pane shown up as table.
- Each line represents a memory allocation event.
- Each column represent an information with allocation. (object type, thread, size)
To be able to see full stack trace of an object is clicking enough.
References: