[Switch] Android analyzes memory usage

Please indicate the source for reprinting: http://blog.csdn.net/guolin_blog/article/details/42238633

Since Android is an operating system developed for mobile devices, we should always take memory issues into consideration 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 free memory. Even if we all followed the programming advice given in the previous article to write the program, there is still a high chance of memory leaks 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, then this article will teach you how to analyze it. If you haven't read the previous article, it is recommended to read Android Best Performance Practices (1) - Manage Memory Reasonably  .

Although the memory of mobile phones is very large now, we all know that it is impossible for the system to allocate all the memory to our applications. That's right, every program has an upper limit on the amount of memory it can use, which is called the Heap Size. Different mobile phones have different heap sizes. With the continuous improvement of hardware devices, the heap size has changed from 32MB for Nexus One to 192MB for Nexus 5. If you want to know what the heap size of your phone is, you can call the following code:

  1. ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);  
  2. int heapSize = manager.getLargeMemoryClass();  
The result is returned in MB, and the memory we use when developing the application cannot exceed this limit, otherwise an OutOfMemoryError will occur. Therefore, for example, if our program needs to cache some data, the capacity of the cached data can be determined according to the size of the heap.

Let's discuss Android's GC operation. The full name of GC is Garbage Collection, which is also called garbage collection. The Android system will trigger the GC operation at an appropriate time. Once the GC operation is performed, some objects that are no longer used will be recycled. So which objects are considered no longer in use and can be recycled? Let's look at the following picture:

In the figure above, each blue circle represents an object in memory, and the arrows between the circles are their reference relationships. Some of these objects are active, while others are no longer in use. Then the GC operation will start from an object called Roots. All the objects it can access are still in use and should be retained, while other objects are no longer used, as shown in the following figure:

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 in the Android system.

So when will the GC operation be triggered? This is usually determined by the system. We generally do not need to actively notify the system that it should go to GC (although we can do this, as will be discussed below), but we can still monitor the GC process of the system to This is to analyze the current memory state 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 format of the log is as follows:

[plain] view plain copy View code snippets on CODE Derive to my code slice
  1. D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>,  <Pause_time>  
Note that I still use the dalvik virtual machine to illustrate, and the content printed in the case of art is basically similar. .

First of all, the first part is GC_Reason , which is the reason for triggering this GC operation. In general, there are the following 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 a GC operation to release the memory.
  • GC_FOR_MALLOC : When our application needs to allocate more memory, but the existing memory is insufficient, the system will perform a GC operation to release the memory.
  • GC_HPROF_DUMP_HEAP : When the HPROF file is generated, the system will perform the GC operation. We will talk about the HPROF file below.
  • GC_EXPLICIT : This situation is what we mentioned just now, actively notify the system to perform GC operations, such as calling the System.gc() method to notify the system. Or in DDMS, it is also possible to explicitly tell the system to perform GC operations through the tool button.

Next, the second part , Amount_freed , indicates how much memory the system has released through this GC operation.

Then Heap_stats will display the current free proportion of memory and usage (memory occupied by active objects / total memory of the current program).

Finally , Pause_time indicates the time that the GC operation caused the application to pause. Regarding this pause time, Android has performed an optimization in version 2.3. Before 2.3, GC operations cannot be performed concurrently, that is, the system is performing GC, so the application can only block and wait for the GC to end. Although this blocking process is not very long, that is, hundreds of milliseconds, users may still feel a little stuck when using our program. Since 2.3, the GC operation has been changed to a concurrent method, that is to say, the GC process will not affect the normal operation of the application, but it will be blocked for a short period of time at the beginning and end of the GC operation, but it is optimized to At this level, users are completely invisible.

The following is the log printed in LogCat for a GC operation:

It can be seen that it is exactly the same as the format we introduced above. The last pause time is 31ms+7ms, one is the pause time at the beginning of the GC, and the other is the pause time at the end. In addition, the GC operation in which program is performed can be distinguished according to the process id. From the above figure, it can be seen that this GC log belongs to the program 24699.

Then this is the GC log printed when the dalvik runtime environment is used, and the art runtime environment has been added since Android 4.4. Printing the GC log in art is basically the same as that of dalvik, as shown in the following figure:

I believe there is nothing difficult to understand. In art, only the format of the content display has changed slightly, and the main content of the print is still the same.

Ok, we can simply understand the GC work of the system through the log, but if we want to know the memory usage of the current application more clearly and in real time, only through the log is not enough, we need to provide it through the DDMS tools to achieve.

Open the DDMS interface, select the application process you want to observe in the left panel, then click the Update Heap button, then click the Heap tab in the right panel, and then keep clicking the Cause GC button to observe the application memory in real time. It can be used, as shown in the following figure:

Then continue to operate our application, and then continue to click the Cause GC button. If you find that repeatedly operating a certain function will cause the application memory to continue to increase without decreasing, then it means that there is a possibility of a memory leak here.

Well, after discussing GC, let's discuss the problem of memory leaks in Android. What everyone needs to know is that the garbage collection mechanism in Android cannot prevent the occurrence of memory leaks. The main reason for memory leaks is that some long-lived objects hold references to other objects that should be recycled, causing the garbage collector to If these objects cannot be reclaimed, there will be a memory leak. For example, a system component like Activity will contain a lot of controls and even pictures. If it cannot be recycled by the garbage collector, it will be considered a serious memory leak.

Let's simulate a scenario of Activity memory leak. I believe everyone has used the inner class. If we define a non-static inner class in a class, then this inner class will hold a reference to the outer class, as follows shown:

  1. publicclass MainActivity extends ActionBarActivity {   
  2.   
  3.     @Override  
  4.     protectedvoid onCreate(Bundle savedInstanceState) {   
  5.         super .onCreate (savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         LeakClass leakClass = new LeakClass();  
  8.     }  
  9.   
  10.     class LeakClass {  
  11.   
  12.     }  
  13.     ......  
  14. }  
At present, there is no problem with the code, because although the inner class LeakClass holds a reference to MainActivity, as long as it does not live longer than MainActivity, it will not prevent MainActivity from being recycled by the garbage collector. So now let's modify the code as follows:
  1. publicclass MainActivity extends ActionBarActivity {   
  2.   
  3.     @Override  
  4.     protectedvoid onCreate(Bundle savedInstanceState) {   
  5.         super .onCreate (savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         LeakClass leakClass = new LeakClass();  
  8.         leakClass.start();  
  9.     }  
  10.   
  11.     class LeakClass extends Thread {  
  12.   
  13.         @Override  
  14.         publicvoid run() {   
  15.             while (true) {  
  16.                 try {  
  17.                     Thread.sleep(60 * 60 * 1000);  
  18.                 } catch (InterruptedException e) {  
  19.                     e.printStackTrace ();  
  20.                 }  
  21.             }  
  22.         }  
  23.     }  
  24.     ......  
  25. }  
This is a bit different. We let LeakClass inherit from Thread, and rewrite the run() method, and then start the LeakClass thread in the onCreate() method of MainActivity. The run() method of LeakClass runs an infinite loop, which means that the thread will never end, so the LeakClass object will never be released, and the MainActivity it holds will not be released, then the memory The leak happened.

Now we can run the program, and then continuously rotate the phone to switch the program between landscape and portrait screens, because every time an Activity is switched, it will go through a process of re-creation, and the previously created Activity cannot be recycled, so long Under the time operation, the memory occupied by our application will become higher and higher, and eventually an OutOfMemoryError will occur.

Below I post a graph of the result of GC log printing when switching between horizontal and vertical screens, as shown below:

It can be seen that the memory occupied by the application is constantly rising. The most terrifying thing is that once the memory goes up, it will never come down again until the program crashes, because this part of the leaked memory can never be reclaimed by the garbage collector.

Then through the two methods of GC logs and DDMS tools learned above, we can now easily find out whether there is a memory leak in the application. But if there is a memory leak, how should we locate the specific problem? This requires the help of a memory analysis tool called Eclipse Memory Analyzer (MAT). We need to download this tool first, the download address is: http://eclipse.org/mat/downloads.php . This tool is divided into Eclipse plug-in version and independent version. If you are using Eclipse to develop, you can use the plug-in version of MAT, which is very convenient. If you are using Android Studio to develop, then you can only use the standalone version of MAT.

After the download is complete, let's start to learn how to analyze the causes of memory leaks. First, enter the DDMS interface, then select the application process we want to observe in the left panel, and then click the Dump HPROF file button, as shown below:

After clicking this button, we need to wait for a while, and then an HPROF file will be generated, which records all the data inside our application. But at present, MAT still cannot open this file. We also need to convert this HPROF file from Dalvik format to J2SE format. The hprof-conv command can be used to complete the conversion, as shown below:

[plain] view plain copy View code snippets on CODE Derive to my code slice
  1. 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 also directly open the generated HPROF file in Eclipse without going through the format conversion step.

Ok, then we can try to use the MAT tool to analyze the cause of the memory leak. Here we need to remind everyone that MAT will not tell us exactly where the memory leak occurred, but will provide a lot of Data and clues, we need to analyze the data ourselves to determine whether a memory leak has really occurred. So now run the MAT tool, and then choose to open the converted-dump.hprof file after conversion, as shown in the following figure:

MAT provides a lot of functions, here we only need to learn a few of the most commonly used ones. The pie chart in the center of the above figure shows the proportion of memory occupied by the largest objects. There is not much content provided in this figure, and we can ignore it. Below this pie chart there are several very useful tools, let's learn about them.

Histogram can list the name, number and size of each object in memory.

Dominator Tree will sort all in-memory objects by size, and we can analyze the reference structure between objects.

Generally, the most commonly used functions are the above two functions, so let's start with the Dominator Tree.

Now click on Dominator Tree and the result will look like this:

This picture contains a lot of information, I will take you to analyze it together. First of all, Retained Heap represents the total memory occupied by this object and other references it holds (including direct and indirect). Therefore, from the above figure, the Retained Heap in the first two lines is the largest. When we analyze the memory leak, the memory The biggest object is also the most suspicious.

In addition, you should be able to notice that there is a file-type icon at the far left of each line. Some of these icons have a red dot in the lower left corner, and some do not. Objects with red dots indicate that they can be accessed by GC Roots. According to the above explanation, objects that can be accessed by GC Roots cannot be recycled. So does this mean that all reddish objects are leaked objects? Of course not, because some object systems need to be used all the time and should not be recycled in the first place. We can notice that all the objects with red dots in the above figure have a System Class written on the far right, indicating that this is an object managed by the system, not an object created by us and causing memory leaks.

So the reason for the memory leak cannot be seen in the above picture? Indeed, memory leaks are not so easy to find, and we need further analysis. In the above figure, in addition to the row with System Class, the largest is the Bitmap object in the second row. Although the Bitmap object cannot be accessed by GC Roots now, it does not mean that other references held by Bitmap will not. Accessed by GC Roots. Now we can right-click on the second row -> Path to GC Roots -> exclude weak references, why choose exclude weak references? Because weak references will not prevent objects from being reclaimed by the garbage collector, we directly exclude them here, and the result is shown in the following figure:

It can be seen that 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 means that it can be accessed by GC Roots, and this is created by us. Thread is not a System Class anymore, so because MainActivity$LeakClass can be accessed by GC Roots, it cannot be recycled, so other references it holds cannot be recycled, including MainActivity and the pictures contained in MainActivity.

In this way, we successfully found the cause of the memory leak. This is a commonly used analysis method in Dominator Tree, that is, searching for the path of large memory objects leading to GC Roots, because objects with higher memory usage are more suspicious.

Next, let's learn the usage of Histogram, go back to the Overview interface, click Histogram, and the result is as shown below:

Here, the names, quantities and sizes of all objects in the current application are listed. It should be noted that the objects here only have Shallow Heap and no Retained Heap, so what does Shallow Heap mean? It is the size of the memory occupied by the current object itself, not including the reference relationship. For example, in the above figure, the Shallow Heap of the byte[] object is the highest, indicating that our application uses a lot of byte[] type data, such as pictures. You can check who is using these byte[] by right clicking -> List objects -> with incoming references.

So how to analyze the cause of memory leak through Histogram? Of course, we can also use a method similar to that in Dominator Tree, that is, to analyze objects with large memory. For example, the byte[] object in the above figure occupies a high memory. By analyzing byte[], we can finally find the location of the memory leak. But here I am going to use another way that is more suitable for Histogram. As you can see, the number of objects can be displayed in the Histogram. For example, if we suspect that there may be a memory leak in MainActivity, we can search for "MainActivity" in the regular expression box on the first line, as shown below:

As you can see, all objects containing the word "MainActivity" are listed here, and the first line is the instance of MainActivity. But have you noticed that there are 11 instances of MainActivity in the current memory, which is too abnormal. In this case, an Activity should have only one instance. In fact, these objects are generated by the constant horizontal and vertical screen switching just now. Because the horizontal and vertical screen is switched once, the Activity will undergo a process of re-creation. However, due to the existence of LeakClass, the previous Activity cannot be recycled by the system, so it appears This is a situation where there are multiple instances of an Activity.

Next, right-click on MainActivity -> List objects -> with incoming references to view the specific MainActivity instance, as shown in the following figure:

If you want to check the specific cause of the memory leak, you can right-click on any instance of MainActivity -> Path to GC Roots -> exclude weak references, the result is shown in the following figure:

As you can see, we found the cause of the memory leak again, which was caused by the MainActivity$LeakClass object.

Well, this is probably some of the most commonly used usages of the MAT tool. Of course, I would like to remind everyone that tools are dead and people are alive. MAT has no way to guarantee that the cause of the memory leak can be found out, or it is necessary to We know enough about the code of the program to know which objects are surviving and why they survive, and then combine the data given by MAT for specific analysis, so that it is possible to hide some deeply hidden objects. Find out the cause of the problem.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326280001&siteId=291194637