Android Best Performance Practice (II)-analyzes memory usage and android Best Performance
Reprinted please indicate the source: http://blog.csdn.net/guolin_blog/article/details/42238633
Because Android is an operating system developed for mobile devices, we should always fully consider the memory issue when developing applications. Although the Android system has an automatic garbage collection mechanism, this does not mean that we can completely ignore when to allocate or release memory. Even if we write the program according to the programming suggestions given in the previous article, there may still be Memory leakage or other types of memory problems. Therefore, the only way to solve the problem is to try to analyze the memory usage of the application, so this article will teach you how to analyze it. If you have not read the previous article, read it first.Android best performance practices (I)-rational Memory Management.
Although the current mobile phone memory is already very large, we all know that the system cannot allocate all the memory to our applications. Yes, each program has a maximum memory available, which is called Heap Size ). Different mobile phones have different heap sizes. As the number of hardware devices increases, the heap size has changed from 32 MB at Nexus One to 192 MB at Nexus 5. If you want to know the heap size of your mobile phone, you can call the following code:
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);int heapSize = manager.getLargeMemoryClass();
The result is returned in MB. The memory we use when developing an application cannot exceed this limit. Otherwise, an OutOfMemoryError occurs. Therefore, for example, if our program needs to cache some data, we can determine the cache data capacity based on the heap size.
Next we will discuss the GC operation in Android. The full name of GC is Garbage Collection, which is also called Garbage Collection. The Android system will trigger the GC operation at the right time. Once the GC operation is performed, some unused objects will be recycled. Which objects are considered to be no longer in use and can be recycled? Let's take a look at the figure below:
Each blue circle represents an object in memory, and the arrows between the circles represent their reference relationships. Some of these objects are active, and some are no longer used. Then the GC operation will start to check from an object named "Roots". All objects accessible to it indicate they are still in use and should be retained, other objects are no longer used, as shown in:
It can be seen that all the yellow objects will still be retained by the system, and the blue objects will be recycled by the system during the GC operation, this is probably a simple GC process of the Android system.
When will the GC operation be triggered? This is usually determined by the system. Generally, we do not need to actively notify the system to perform GC (although we can do this, we will discuss it below ), however, we can still monitor the GC process of the system to analyze the current memory status of our application. So how can we monitor the GC process of the system? In fact, it is very simple. Every time the system performs a GC operation, it will print a log in LogCat. We only need to analyze this log. The basic log format is as follows:
D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <Pause_time>
Note that I still use the dalvik Virtual Machine to describe the content printed in art ..
First, part 1GC_ReasonThis is the reason for triggering the GC operation. Generally, there are several reasons for triggering the GC operation:
- GC_CONCURRENT: When the heap memory of our application is about to be full, the system will automatically trigger the GC operation to release the memory.
- GC_FOR_MALLOC: When our applications need to allocate more memory, but the existing memory is insufficient, the system will perform GC operations to release the memory.
- GC_HPROF_DUMP_HEAP: When the HPROF file is generated, the system will perform GC operations. We will discuss the HPROF file below.
- GC_EXPLICIT: As we mentioned earlier, the System is notified to perform GC operations. For example, the System. gc () method is called to notify the System. Or in DDMS, the tool button can also explicitly tell the system to perform GC operations.
Part 2Amount_freedIndicates how much memory the system has released through this GC operation.
ThenHeap_statsDisplays the current memory idle ratio and usage (Active Object Memory/total program memory ).
LastPause_timeIndicates the time when the application is suspended due to this GC operation. For the pause time, Android has been optimized once in Version 2.3. Before Version 2.3, GC operations cannot be performed concurrently, that is, the system is performing GC, then the application can only block and wait until GC ends. Although the blocking process is not very long, that is, several hundred milliseconds, users may feel a little stuck when using our program. After 2.3, the GC operation was changed to the concurrent mode, that is, the GC process does not affect the normal operation of the application, however, the GC will be blocked for a short period of time at the beginning and end of the operation. However, to this degree of optimization, the user is completely unaware of it.
The following is the log printed by a GC operation in LogCat:
It can be seen that the format is exactly the same as the format described above. The last pause time is 31 ms + 7 ms, and the pause time at the beginning of GC is one time, one is the pause time at the end. In addition, you can identify the program in which the GC operation is performed based on the process id, so we can see that this GC log belongs to the program 24699.
This is the GC log that is printed when dalvik is used to run the environment. Since Android 4.4 and later, the GC log is added to the art runtime environment. In art, it is basically the same as that in dalvik, as shown in:
I believe there is nothing difficult to understand. In art, only the content display format has changed slightly, and the printed content remains unchanged.
Okay. We can get a brief picture of the GC operation of the system through the LOG method, but if we want to know the memory usage of the current application in real time more clearly, logs are insufficient. We need to use the tools provided in DDMS.
Open the DDMS interface, select the application process you want to observe in the left-side pane, and then clickUpdate HeapClick the Heap tag in the right pane, and then click the Cause GC button to observe the application memory usage in real time, as shown in:
Continue to operate our application, and then clickCause GCButton. If you find that repeated operations on a function will cause the application's memory to continuously increase without decreasing, it indicates that the memory may leak.
Now, after discussing GC, let's talk about memory leakage in Android. What you need to know is that the garbage collection mechanism in Android cannot prevent memory leakage, the main cause of Memory leakage is that some persistent objects hold references to other objects that should be recycled. As a result, the garbage collector cannot recycle these objects, which leads to memory leakage. For example, a system component like Activity may contain many widgets or even images. If it cannot be recycled by the garbage collector, it may be a serious memory leakage.
Next we will simulate a scenario of Activity Memory leakage. I believe all internal classes are useful. If we define another non-static internal class in a class, the internal class will hold the reference of the external class as follows:
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LeakClass leakClass = new LeakClass(); } class LeakClass { } ......}
At present, the code is still correct, because although the internal class LeakClass holds the reference of MainActivity, as long as its survival time is not longer than MainActivity, it will not prevent MainActivity from being recycled by the garbage collector. Now let's modify the Code as follows:
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LeakClass leakClass = new LeakClass(); leakClass.start(); } class LeakClass extends Thread { @Override public void run() { while (true) { try { Thread.sleep(60 * 60 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ......}
This is a bit different. Let's let LeakClass inherit from Thread, rewrite the run () method, and then start the LeakClass Thread in the onCreate () method of MainActivity. The run () method of LeakClass runs an endless loop. That is to say, this thread will never end, so the LeakClass object will never be released, and the MainActivity it holds will not be released, so the memory leakage will occur.
Now we can run the program and rotate the mobile phone constantly so that the program can be switched between the landscape screen and the portrait screen, because every time the Activity is switched, it will go through a re-creation process, the previously created Activity cannot be recycled, so the memory occupied by our application will become higher and higher after a long operation, and an OutOfMemoryError will eventually occur.
The following shows the result of GC log printing when the screen is continuously switched to landscape and landscape, as shown below:
As you can see, the memory occupied by applications is constantly increasing. The most terrible thing is that once the memory is increased, it will never drop until the program crashes, because the leaked memory cannot be recycled by the garbage collector.
Now we can easily find out whether memory leakage exists in the application by using the GC logs and DDMS tools learned above. But if there is a memory leak, how should we locate the specific problem? This requires a Memory analysis tool called Eclipse Memory Analyzer (MAT ). Download the tool: http://eclipse.org/mat/downloads.php. This tool is divided into two types: Eclipse plug-in version and independent version. If you are using Eclipse for development, you can use the plug-in version MAT, which is very convenient. If you are using Android Studio, you can only use the independent MAT version.
After the download, we will learn how to analyze the cause of Memory leakage. First, go to the DDMS page, select the application process we want to observe on the left-side pane, and then clickDump HPROF fileAs shown in:
After clicking this button, you need to wait for a while and then generate an HPROF file, which records all the data in our application. However, currently, MAT still cannot open this file. We also need to convert the HPROF file from Dalvik format to J2SE format and use the hprof-conv command to complete the conversion, as shown below:
hprof-conv dump.hprof converted-dump.hprof
The hprof-conv command file is stored in the <Android Sdk>/platform-tools directory. In addition, if you are using the plug-in version of MAT, you can directly open the generated HPROF file in Eclipse without going through the format conversion step.
Okay, now we can try to use the MAT tool to analyze the cause of Memory leakage. here we need to remind you that the MAT won't tell us exactly where the memory leakage occurred, instead, it provides a lot of data and clues. We need to analyze the data to determine whether a memory leak has actually occurred. Run the MAT tool now and choose to open the converted converted-dump.hprof file, as shown in:
MAT provides many functions. Here we only need to learn a few of the most commonly used functions. The most central pie chart shows the memory ratio of the largest objects. This chart does not provide much content, so we can ignore it. There are several useful tools below this pie chart. Let's take a look.
HistogramLists the names, quantities, and sizes of each object in the memory.
Dominator TreeAll objects in the memory are sorted by size, and we can analyze the reference structure between objects.
Generally, the most common functions are the above two functions, so we should start learning from Dominator Tree.
Click Dominator Tree. The result is as follows:
This figure contains a lot of information. Let's take a look at it. First, Retained Heap indicates the total memory occupied by this object and other references it holds (including direct and indirect). Therefore, the Retained Heap in the first two rows is the largest, when we analyze memory leaks, the biggest objects in the memory should also be suspected.
In addition, you should note that there is a file icon on the far left of each row. Some of these icons have a red dot in the lower left corner, while others do not. Objects with red dots indicate that they can be accessed by GC Roots. According to the above explanation, all objects that can be accessed by GC Root cannot be recycled. So does this mean that all objects with red are leaked objects? Of course not, because some object systems need to be used all the time, they should not be recycled. We can note that all objects with red points write a System Class on the rightmost side, indicating that this is an object managed by the System, it is not an object created by ourselves that causes memory leakage.
So can't you see the cause of Memory leakage? Indeed, the memory leakage is not so easy to find. We need further analysis. In addition to the row with System Class, the largest is the Bitmap object of the second row. Although the Bitmap object cannot be accessed by GC Roots, however, it does not mean that other references held by Bitmap will not be accessed by GC Roots. Now we can right-click the second line-> Path to GC Roots-> exclude weak references. Why do we select exclude weak references? Weak references do not prevent objects from being recycled by the garbage collector, so we will exclude them directly here, as shown in the result:
As you can see, after the Bitmap object is referenced layer by layer, it reaches the MainActivity $ LeakClass object, and then there is a red icon in the lower left corner of the icon, which indicates that it can be accessed by GC Roots, in addition, this Thread is created by ourselves and is not a System Class. Therefore, the MainActivity $ LeakClass cannot be recycled because it can be accessed by GC Roots, as a result, other references held by it cannot be recycled, including MainActivity and images contained in MainActivity.
In this way, we can find out the cause of Memory leakage. This is a common analysis method in Dominator Tree, that is, to search the path from a large memory object to GC Roots, because the more memory occupied, the more suspicious the object is.
Next, let's take a look at the Histogram usage. Return to the Overview interface and click Histogram. The result is shown in:
The name, quantity, and size of all objects in the current application are listed. Note that only Shallow Heap and no Retained Heap are available, so what does Shallow Heap mean? The size of the memory occupied by the current object, excluding the reference relationship. For example, the Shallow Heap of the byte [] object is the highest, this shows that our application uses a lot of byte [] data, such as images. You can right-click and choose List objects> with incoming references to check who is using these byte [].
How can I analyze the cause of Memory leakage through Histogram? Of course, it can also be used in a similar way as in Dominator Tree, that is, to analyze large memory objects. For example, byte [] has a high memory usage. We analyze byte [], in the end, memory leakage can be found, but here I am going to use another method that is more suitable for Histogram. As you can see, Histogram can display the number of objects. For example, we suspect that there may be Memory leakage in MainActivity, you can search for "MainActivity" in the regular expression box of the first line, as shown below:
As you can see, all objects containing "MainActivity" are listed here. The first line is the instance of MainActivity. However, have you noticed that there are 11 MainActivity instances in the current memory? This is not normal. In this case, only one instance is available for the Activity. In fact, these objects are generated due to the constant horizontal and vertical screen switching. Because the horizontal and vertical screen switching once, the Activity will go through a re-creation process. However, due to the existence of LeakClass, the previous Activity cannot be recycled by the system, so this Activity has multiple instances.
Right-click MainActivity-> List objects-> with incoming references to view the specific MainActivity instance, as shown in:
If you want to view the specific cause of Memory leakage, you can right-click an instance of any MainActivity-> Path to GC Roots-> exclude weak references, as shown in:
We can see that the cause of Memory leakage is found again because of the MainActivity $ LeakClass object.
Well, this is probably the most common usage of the MAT tool. Of course, I would like to remind you that the tool is dead and people are living, there is no way for MAT to ensure that we can find out the cause of Memory leakage. We still need to have enough knowledge about the program code and know which objects are alive, and the cause of their survival, and then analyze the data given by MAT to find out some hidden causes.
So today we have introduced a lot of content. This is the last article of this year because the Spring Festival is coming soon, I wish you a happy Spring Festival. During the holiday, I hope you can put down the code and take a good rest for a while. Therefore, the next article will be updated later to introduce some high-performance coding skills.
Get a blog update reminder and share more technical information. Please follow my public account and scan the QR code or search for guolin_blog.