an excellent Android application, not only to have a perfect function, but also to have a good experience, and performance is an important factor affecting the experience. Memory leaks are a common performance issue in Android development. This article, through a real case we've encountered, tells a memory leak, from discovery to analysis, to the whole process of final resolution.
Here the whole process is divided into four stages:
- The first stage, the scene investigation, analyzes the bug phenomenon, finds the useful clue;
- The second stage, the preliminary inference, based on the previous clues, infer the cause of the bug may be caused, and further verify that the inference is correct;
- The third stage, explore the root cause, find out the real cause of the bug;
- Phase IV, the solution, to study how to solve the problem.
Site Survey
Before we developed an application, to the QA test, found that sometimes the interface will be Kaka, the animation is not smooth. After repeated tests, he found that the problem arose when the application was opened several times in a row. When we reproduce the bug in this way, we also find that the GC log is frequently output in logcat (shown in one).
Figure A
Here is a brief introduction to the GC, which is garbage collection mechanism, Android by providing a garbage collection mechanism to manage memory, when the memory is not enough to trigger garbage collection, recycling useless objects, free memory. We look at the garbage collection process in two graphs (figure two or three).
Here GC roots represents the garbage collector object, each node represents an object in memory, an arrow represents a reference relationship between objects, an object ABCD that can be referenced directly or indirectly by GC roots, represents an object that is being used, EFG that cannot be referenced is a useless object, and garbage collection is recycled. When a garbage collection is triggered by the system, the object EFG is recycled.
The above is the process of garbage collection. At this stage of the site investigation, we found two very useful clues:
- Clue one: After several consecutive open applications, the interface lag, animation is not smooth;
- Clue two: During operation, Logcat frequently outputs GC logs.
Preliminary Inference
Now to the second stage, based on the clues found in the previous phase, when the application is opened multiple times, the interface is stuck, and logcat continuously outputs the GC log, it is preliminarily speculated that there is a memory leak in our application. First, let's look at what a memory leak is. We demonstrate by two sheets, four and five.
This image is similar to the one shown in the GC process just now, when the GC is triggered again, eg is recycled and f is useless for the application and cannot be recycled, resulting in a memory leak.
Therefore, it is likely that every time the application is opened, objects such as f will result in higher memory consumption and the system frequently triggers the GC.
Validation Inference
The next step is to verify that we use the DDMS tool here.
DDMS is a virtual machine Debug monitoring service that helps us to test the screen of a device, set up virtual geographic coordinates, view its heap information for a particular process, and so on.
How can we use it to validate our inferences? First, load out the application memory snapshot, here is divided into 4 steps, the first step, select the application we want to view, the second step to click the Update heap button, this time DDMS will notify the application to collect memory information, the third step to select the heap tag, the Heap tab can show all the information of memory. The fourth step is to click Cause GC, which will load the memory snapshot. This DDMS the memory snapshot load out. Specific Operation six.
Figure Six
After the load out of memory information, we analyze whether there is a memory leak in our application, and one of the key data that analyzes the memory leak is total Size.
When the app is opened repeatedly, total size will only fluctuate within a certain range if there is no memory leak. If our inference is correct and the application is opened continuously, total size will continue to increase. Then we'll test the analysis, open the application continuously, seven.
Figure Seven
This shows the 123th, and the tenth time the total size is opened, and total size is increasing, where the 1-byte array is the most obvious, and the 1-byte array represents an array of byte[], or boolean[] types. So we can conclude that there is a memory leak when the app is opened.
Explore the root cause
Identify the problem and then explore the root cause of the problem. Every application runs with tens of thousands or even millions of objects, and we need to analyze the state of these objects in memory to see which objects are useless to the application, but still occupy the memory.
We used the mat in this process. Mat is a versatile, fast-running heap memory analysis tool. It can quickly analyze all objects in the heap and calculate the amount of memory each object occupies. It's very powerful, and after analyzing the memory, it can also help identify objects that can cause memory leaks, list objects that occupy large memory, and provide the ability to query Java container object usage, which is very helpful for us to analyze the memory of the application.
It has both a standalone installer and a plugin for eclipse, and we download the program according to our needs. We used it very simply, using the DDMS tool we just introduced to export the memory snapshot to the. hprof file, then the mat opens the Hprof file directly. Standalone Installer: http://www.eclipse.org/mat/.
According to the previous test, we have a few operations to cause the 1-byte array total size from 20M to 70M, the average increment of about 5M each time, this size is relatively large, so inferred that there is a large memory footprint of the object caused by memory leaks. In conjunction with the Dominator tree function of the mat, let's start with the analysis, Dominator tree can list all the objects in memory, and the size of the memory they occupy.
Figure Eight
Here is a dominator tree, first introduced two nouns the first shallow heap, which represents the memory size of the object itself, including the object's head and member variables, the second retained Heap represents the sum of the memory of an object itself and all the objects it holds. That is, the GC reclaims all the memory space freed by an object. As you can see from this graph, the retained heap is the largest time resources object, but the resource is the System class object, that is, the systems management object, nor is it the cause of our memory leaks, we don't have to parse it.
The second largest is the bitmap object. We already know from the previous introduction that if an object can be referenced directly or indirectly by GC roots, it cannot be recycled, so let's take a look at the reference path bitmap to the GC roots to see which object is holding the bitmap. Select Bitmap, right-click, Path to GC Roots, and select Execlude weak references, because weak references do not prevent garbage collection, so we directly exclude weak references.
Figure Nine below is the reference path to bitmap to GC roots. There is a small red dot in front of the Loadpicthread object, and this little red dot indicates that the object is directly held by the GC roots.
Figure Nine
So the entire reference path is the GC roots refers to thread,thread referencing our activity, and the activity contains the bitmap object.
The current interface has exited, but thread still has a reference to activity that causes activity and the memory it references, such as bitmap, not to be recycled. At this point the truth of the problem is basically surfaced.
To further confirm our results, we verify from another point of view if there are multiple objects of activity that are held by thread in memory that cannot be recycled. With the histogram function of the mat, it can list all the classes in memory and the number of instances per class.
Figure 10
Ten, Mat provides a regular search function, can be searched according to the class name, we find here the result is 11 activity object, so further validation success. It is because the thread that we created holds the Acitivy object, causing the Activiy to not be recycled after it is closed.
Code Analysis
Based on the above analysis, we found the code that caused the memory leak.
[Java]View Plain Copy
- class testactivity{
- protected void onCreate (Bundle savedinstancestate) {
- loadpicthread loadpicthread = new loadpicthread ();
- Loadpicthread.start ();
- }
- Private class Loadpicthread extends Thread {
- @Override
- Public void run () {
- Super. Run ();
- While (true) {
- .... Load data ...
- try {
- Thread.Sleep (*5);
- } Catch (Interruptedexception e) {
- E.printstacktrace ();
- }
- }
- }
- }
- }
Loadpicthread is an inner class of testactivity, which implicitly holds an instance of testactivity, Loadpicthread will go to the server every 5 minutes to request data, the thread will never end, and each time you open the interface will create a thread like this. So the root cause of the memory leak here is that the long-life-cycle Object (Thread) holds a reference to the short-life-cycle Object (Activity), causing the Activiy to exit and not be recycled.
Solution Solutions
Finally to solve the problem stage, find out how to solve the problem? We think of two solutions.
First, the thread is removed from the activity and can be placed in a background service so that the activity and thread do not depend on each other, and if the thread is doing something that is not very close to the activity business logic, such as in some data caching operations, This is the kind of solution that can be used.
Second, when the activity ends, stop the THEAD, keep the thread in line with the activity's life cycle, and generally you can send an end signal to the thread in the Ondestory method.
Summary
These are the entire process from discovery to resolution of memory leaks. In fact, in the Android development process, a lot of wrong code, causing memory leaks. For example, improper use of context; When constructing adapter, there is no use of cached Convertview and so on.
Finally, summarize:
First, as an Android developer, only a deep understanding of the working mechanism of common Android components, as well as the life cycle of each object in the application, can avoid writing code that causes memory leaks;
Second, when a problem occurs in the program, first to find the scene that triggered it, as in this case, we have been repeatedly tested and observed according to the method of reproduction provided by QA, and finally locate the problem. In our daily development, we may encounter more complex problems, in the face of complex situations, only to find the key to trigger the problem, we can quickly locate the problem and solve.
Third, powerful tools that help us analyze and locate problems, such as the DDMS and mat tools we used earlier, enable us to delve into the application's internal exploration and research to quickly analyze the root cause of the problem. So developers should learn to use these powerful tools to analyze and solve various difficult problems.
Android Memory leak case analysis