Memory leakage of Android applications

Source: Internet
Author: User

Memory leakage of Android applications
Memory leakage of Android applications

Differences between memory leakage and memory overflow

Memory overflow (out of memory)When a program requests memory, it does not have enough memory space for use. For example, when every Android program is running, the system will allocate a certain amount of memory space to the program, when the memory required by the program exceeds this limit, the out of memory will be reported ).
Memory leakage (memory leak)After applying for memory, the program cannot release the applied memory. The memory cannot be released multiple times, and the memory occupied by the program will increase until the system memory limit is exceeded, leading to memory overflow.

Why is memory leakage in java?

When you are learning Java, you can read related books. Among the advantages of Java, Java automatically manages memory collection through GC, the programmer does not need to call a function to release the memory. Therefore, many people think that there is no memory leakage problem in Java. This is not the case in reality, especially when we develop apps related to mobile phones and tablets, it is often caused by the accumulation of memory leaks that quickly lead to program crashes. To understand this problem, we need to first understand how Java manages memory.
Java Memory Management
Java memory management is the issue of object allocation and release. in Java, programmers need to use keywordsNewApply for memory space for each object (except for basic types). All objects are allocated space in Heap. In addition, the release of objects is determined and executed by GC. In Java, memory allocation is completed by the program, and memory release is completed by GC. This sort of revenue and expenditure does simplify the work of programmers. But at the same time, it also increases the JVM burden. This is one of the reasons why Java runs slowly. To release each object correctly, GC must monitor the running status of each object, including object application, reference, reference, and assignment.
The object state is monitored to release objects more accurately and timely. The fundamental principle of releasing an object is that the object is no longer referenced.
To better understand the working principle of GC, we can consider the object as the vertex of the directed graph, consider the reference relationship as the directed edge of the graph, and direct the directed edge from the quotor to the cited object. In addition, each thread object can be used as the starting vertex of a graph. For example, if most programs start to run from the main process, this graph is a root tree starting from the vertex of the main process. In this directed graph, the objects accessible to the root vertex are all valid objects, and GC will not recycle these objects. If an object (connected subgraph) is inaccessible to this root vertex (note that this graph is a directed graph), we think this (these) object is no longer referenced and can be recycled by GC.
The following example shows how to use a directed graph to represent memory management. For every moment of the program, we have a directed graph to indicate the memory allocation of the JVM. The right figure below shows that the program runs on the left to 6th rows.

Java uses Directed Graphs for memory management to eliminate the issue of reference loops. For example, if there are three objects that reference each other, as long as they are inaccessible to the root process, GC can also recycle them. The advantage of this method is that the memory management accuracy is high, but the efficiency is low. Another common memory management technology is the use of counters. For example, the COM model uses counters to manage components. Compared with Directed Graphs, it has low precision rows (it is difficult to handle the issue of circular references ), however, the execution efficiency is high.
Memory leakage in Java
Next, we can describe what is memory leakage. In Java, memory leakage is the existence of some allocated objects. These objects have the following two features: first, these objects are reachable, that is, in a directed graph, there are paths that can be connected to them; second, these objects are useless, that is, they will not be used by the program in the future. If the objects meet these two conditions, these objects can be identified as memory leaks in Java. These objects are not recycled by GC, but occupy the memory.
In C ++, the memory leakage scope is larger. Some objects are allocated with memory space but cannot be accessed. Because C ++ does not have GC, these memories will never be recovered. In Java, GC is responsible for the collection of these inaccessible objects, so programmers do not need to consider the memory leakage.
Through analysis, we know that for C ++, programmers need to manage edges and vertices themselves, while Java programmers only need to manage edges (no need to manage the release of vertices ). In this way, Java improves programming efficiency.


Therefore, through the above analysis, we know that memory leakage also exists in Java, but the range is smaller than that in C ++. Because Java ensures that all objects are reachable, and all unreachable objects are managed by GC.
For programmers, GC is basically transparent and invisible. Although only a few functions can access GC, for example, the System. GC () function that runs gc, according to Java language specification definition, this function does not guarantee that the JVM garbage collector will certainly execute. Because different JVM implementers may use different algorithms to manage GC. Generally, the priority of GC threads is low. There are also many policies for JVM to call GC. Some of them start to work when the memory usage reaches a certain level, some are scheduled, some are gentle execution of GC, and some are interrupted execution of GC. But in general, we don't need to care about this. Unless in some specific scenarios, GC execution affects the performance of applications, such as Web-based real-time systems, such as online games, if you do not want the GC to suddenly interrupt application execution and perform garbage collection, you need to adjust the GC parameters so that the GC can release the memory in a gentle manner, for example, if garbage collection is divided into a series of small steps for execution, Sun's HotSpot JVM supports this feature.

Android Memory leakage Summary
When developing Android programs, we often encounter memory overflow. During our Launcher development process, there is a memory leakage problem. Next we will share with you how to solve the memory leakage based on the actual problems we encountered during Launcher development.

Common Memory leakage in Android

Collection class Leakage

If a collection class only has methods for adding elements, but does not have a corresponding deletion mechanism, the memory is occupied. If this collection class is a global variable (such as static attributes in the class and Global map), there is no corresponding deletion mechanism, it is likely that the memory occupied by the set will only increase or decrease. Please refer to the following sample code. If you do not pay attention to it, this situation is very likely to happen. For example, we all like to use HashMap to do some caching and other things. In this case, you need to stay focused on it.

    ArrayList list = new ArrayList();    for (int i = 1; i < 100; i++) {        Object o = new Object();        v.add(o);        o = null;       }

In the code above, list is a global variable. It is useless to leave the object reference blank later, because the object is held by list. If the lifecycle of this class is not over, it will not be recycled by gc.

Memory leakage caused by a single instance.

Because of the Static Characteristics of a single instance, the lifecycle of the instance is as long as that of the application, so if you use it improperly, the objects held by the single instance will always exist, which may easily cause memory leakage. For example, the following is a typical example:

    public class AppManager {        private static AppManager instance;        private Context context;        private AppManager(Context context) {            this.context = context;        }        public static AppManager getInstance(Context context) {        if (instance == null) {        instance = new AppManager(context);        }        return instance;        }        }

In the preceding example, if the input Context is Activity and AppManager is a static variable, its lifecycle is the same as that of Application. Because this Activity has been held by this instance, the uploaded Activity cannot be recycled. Memory leakage will occur. Solution: The Application can be passed in here, because the life cycle of the Application starts from the beginning to the end.

Memory leakage caused by non-static internal class creation of static instances

A non-static internal class holds this class by default. If its instance is static in this class, it indicates that its lifecycle is as long as that of the Application. By default, static instances of non-static internal classes hold this class, and the resource will not be gc, resulting in Memory leakage.

public class MainActivity extends Activity{     private static LeakInstance mLeakInstance;     @override     public void onCreate(Bundle onsaveInstance){        ......     }     class LeakInstance{         .....     }}

In the code snippet above, LeakInstance is an internal class in the Activity. It has a static instance, mLeakInstance. the lifecycle of the static instance is the same as that of the Application, at the same time, it holds MainActivity by default, which will prevent the Activity from being gc and cause memory leakage.

Anonymous internal classes run in asynchronous threads.

By default, an anonymous internal class holds the reference of its class. If the anonymous internal class is put in a thread for running, and the life cycle of this thread is different from that of this class, this class is held by the thread and cannot be released. Memory leakage. See the following sample code:

public class MainActiviy extends Activity{   private Therad mThread = null;   private Runnable myRunnable = new Runnable{        public void run{           ......        }   }       protected void onCreate(Bundle onSaveInstance){               .......              mThread = new Thread(myRunnable);              mThread.start();       }}

In the preceding example, myRunnable holds MainActiviy. The mThread lifecycle is different from that of Activity, and MainActiviy is held until the Thread stops running. Memory leakage.

Memory leakage caused by Handler

The memory leakage caused by Handler usage should be said to be the most common. In many cases, we do not perform time-consuming operations on the main thread to avoid ANR, handler is used to process network tasks or encapsulate some request callbacks and other APIs. However, Handler is not omnipotent. If Handler is not compiled using code, memory leakage may occur. In addition, we know that Handler, Message, and MessageQueue are all associated with each other. In case that the Message sent by Handler is not processed, the Message and the Handler object sent to it will be held by the thread MessageQueue.
Because Handler is a TLS (Thread Local Storage) variable, the lifecycle and Activity are inconsistent. Therefore, this implementation method is generally difficult to ensure that it is consistent with the life cycle of the View or Activity, so it is easy to cause the release to fail correctly. Let's take a look at the following columns:

public MainActivity extends Activity{    private Handler mHandler = new Handler();    protected void onCreate(Bundle onSaveInstance){        .......        mHandler.postDelay(new Runnable(){        },1000*1000);    }}

In the above Code, mHandler has delay for a long time and actually holds MainActivity. If this Activity dies, it cannot be recycled. Wait for mHandlder to release the held resources.

How to detect memory leakage MAT

1. directly observe the memory of the Android Monitor. For example, when developing the Launcher, the Launcher Activity experienced a memory leakage during horizontal/vertical screen switching, at this time, the Memory value will keep increasing, and the Memory cannot be released by manually clicking GC.

Or you can also observe it in DDMS.

2. Use the MAT tool to find
There are many Java Memory Leak Analysis Tools, but we all know that we need to use MAT (Memory Analysis Tools) and YourKit.
MAT analyzes the total memory usage of heap to determine whether leakage exists.
Open the DDMS tool, select the "Update Heap" icon on the Devices view on the left, switch to the Heap view on the right, and click "Cause GC" in the Heap view, so far, the process to be detected can be monitored.

In the Heap view, there is a Type called data object, that is, the data object, which is a large number of class objects in our program. In a row of data object, there is a column named "Total Size", whose value is the Total memory of all Java data Objects in the current process. Generally, the size of this value determines whether memory leakage exists. You can judge this as follows:
Go to an application and constantly operate the application. Observe the Total Size value of the data object. Under normal circumstances, the Total Size value will be stable within a limited range, that is to say, because the code in the program is good, the object is not recycled.
Therefore, although we continuously generate many objects during operations, these objects are recycled during the continuous GC of virtual machines, the memory usage will fall to a stable level. Otherwise, if the reference of the object is not released in the Code, the Total Size of the data object will not drop significantly after each GC. As the number of operations increases, the value of Total Size increases until a maximum value is reached, causing the process to be killed.
MAT analyzes hprof to identify the cause of Memory leakage

This is an effective way to use MAT to locate problems after memory leaks.
A) Dump out the memory to leak the memory image hprof at that time, and analyze the class suspected to be leaked:

B) analyze external objects that hold such object references

C) analyze the GC paths of these referenced objects

D) check whether the GC path of each object is normal one by one

From this path, we can see that the reference of MainActivity is held by an antiRadiationUtil tool class object, and thus the MainActivity cannot be released. In this case, you need to go to the code analysis. In this case, is the reference of antiRadiationUtil reasonable? (If antiRadiationUtil holds the context of MainActivity, And the MainActivity cannot be destroyed after the program exits, the memory is generally leaked ).
MAT compares hprof before and after the operation to locate the root cause of Memory leakage
To find memory leaks, we usually need to Compare two Dump results. Open the Navigator History panel and add the Histogram results of both tables to Compare Basket.
A) The first HPROF file (usingFile> Open Heap Dump ).
B) Open Histogram view.
C) In the NavigationHistory view (from Window> show view> MAT-Navigation History if not visible), right-click histogram and select Add to Compare Basket.

D) open the second HPROF file and redo steps 2 and 3.
E) switch to the Compare Basket view and click Compare the Results (red in the upper right corner of the view "!" Icon ).

F) analysis and comparison results

We can see the comparison results of two hprof data objects.
In this way, you can quickly locate the object increment held before and after the operation to further locate the specific cause of Memory leakage caused by the current operation is the leaked data object.
Note:
If the Dump file is obtained using the MAT Eclipse plug-in, it can be opened in MAT without conversion. The Adt will automatically convert.
The Dump file of the mobile SDk must be converted before it can be recognized by MAT. The Android SDK provides the hprof-conv tool (under sdk/tools)
First, go to your android sdk tools directory on the console and run the following command:
./Hprof-conv xxx-a.hprof xxx-b.hprof
For example, hprof-conv input. hprof out. hprof
In this case, the out. hprof can be opened in the MAT of eclipse.
The following describes LeakCanary, a dashboard tool.

LeakCanary

What is LeakCanary? Why is it used to detect Android Memory leakage?
Don't worry. Let me tell you something slowly!
LeakCanary is an open-source class library developed by another foreign god, Pierre-Yves Ricau, to detect memory leaks. In general, in the case of Memory leakage, we will go through the following key steps:
1. Learn about OutOfMemoryError.
2. Reproduce the problem.
3. Dump out the memory when Memory leakage occurs.
4. Dump out the memory when Memory leakage occurs.
5. Calculate the shortest strong reference path from this object to GC roots.
6. Determine which reference in the reference path should not be available and fix the problem.
Complicated, right?
If there is a class library that can solve all these things before OOM occurs, then you only need to fix these problems. LeakCanary is doing this. You can easily detect memory leaks in the debug package.
Let's take a look at this example (from LeakCanary's Chinese instructions, and we will attach a link to all the reference documents below ):

Class Cat {} class Box {Cat hiddenCat;} class Docker {// static variable, with the same lifecycle as Classload. Static Box cainter;} // Cat schrodingerCat = new Cat (); box. hiddenCat = schrodingerCat; Docker. container = box;

Create a RefWatcher to monitor object references.

// We expect the cat to disappear soon (or not). Let's monitor refWatcher. watch (schrodingerCat );

When a memory leak is detected, you will see a very beautiful leak trace report:
Gc root static Docker. container
References Box. hiddenCat
Leaks Cat instance
We know that you are very busy and have a lot of demands every day. So we can make it simple. You just need to add a line of code. Then LeakCanary will automatically detect activity Memory leakage.

public class ExampleApplication extends Application {  @Override public void onCreate() {    super.onCreate();    LeakCanary.install(this);  }}

Then you will see a pretty interface in the notification bar:

In a straightforward way, the memory leakage is shown in front of us.
Demo

A very simple LeakCanary demo: a very simple LeakCanary demo: https://github.com/liaohuqiu/leakcanary-demo
Access

Add references to build. gradle. Different references are used for different compilation:

 dependencies {   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3' }

How to Use

Use RefWatcher to monitor objects that are supposed to be recycled.

RefWatcher refWatcher = {...}; // monitor refWatcher. watch (schrodingerCat );

LeakCanary. install () returns a pre-defined RefWatcher and enables ActivityRefWatcher to automatically monitor the Activity leaked after activity. onDestroy () is called.
Configure in Application:

public class ExampleApplication extends Application {  public static RefWatcher getRefWatcher(Context context) {    ExampleApplication application = (ExampleApplication) context.getApplicationContext();    return application.refWatcher;  }  private RefWatcher refWatcher;  @Override public void onCreate() {    super.onCreate();    refWatcher = LeakCanary.install(this);  }}

Use RefWatcher to monitor Fragment:

public abstract class BaseFragment extends Fragment {  @Override public void onDestroy() {    super.onDestroy();    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());    refWatcher.watch(this);  }}

Use RefWatcher to monitor Activity:
Public class MainActivity extends AppCompatActivity {

...... @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); // Add the following two lines of code to the initial Activity of your application: RefWatcher refWatcher = ExampleApplication. getRefWatcher (this); refWatcher. watch (this); textView = (TextView) findViewById (R. id. TV); textView. setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {startAsyncTask () ;}) ;} private void async () {startAsyncTask ();} private void startAsyncTask () {// This async task is an anonymous class and therefore has a hidden reference to the outer // class MainActivity. if the activity gets destroyed before the task finishes (e.g. rotation), // the activity instance will leak. new AsyncTask
  
   
() {@ Override protected Void doInBackground (Void... params) {// Do some slow work in background SystemClock. sleep (20000); return null;} cmd.exe cute ();}
  

}

Working mechanism

1. RefWatcher. watch () creates a KeyedWeakReference to the object to be monitored.
2. Check whether the reference is cleared in the background thread. If not, call GC.
3. If the reference is still not cleared, dump the heap memory to A. hprof file in the file system corresponding to the APP.
4. In another process, HeapAnalyzerService has a HeapAnalyzer that uses HAHA to parse the file.
5. Thanks to the unique reference key, HeapAnalyzer finds the KeyedWeakReference to locate Memory leakage.
6. HeapAnalyzer calculates the shortest strong reference path of GC roots and determines whether the path is leaked. If yes, create a reference chain that causes leakage.
7. The reference chain is passed to the DisplayLeakService in the APP process and displayed as a notification.
OK. I will not go deep here. If you want to know more, go to the author's github homepage.

Androidstudio built-in analysis tools

Use the built-in Memory tool in Android Monitor, click GC as shown in the figure, and then generate the hprof file.

Then, double-click the generated file.

We can quickly find out the cause of Memory leakage.

A memory overflow Column

The following shows a specific case of Memory leakage.
Create a new project in Android Studio and create an APPManager Singleton class:

public class AppManager {    private static Context sContext;    private static AppManager instance;    public  static AppManager getInstance(Context context){        if(instance==null){            instance = new AppManager(context);        }        return instance;    }    private AppManager(Context context){        sContext = context;    }}

In the preceding code snippet, Context is written in the class as a static variable. Continue with the following code:

 protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(toolbar);        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);        fab.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)                        .setAction("Action", null).show();            }        });        AppManager.getInstance(this);    }

At this time, we call the portrait screen multiple times and then find
We usually use this class as in the above instance code in the Activity. Let's call the MAT analysis tool to analyze the above Code:
Step 1:
Run the above Code. After multiple screen views, click.

After the preceding operations

Here we need to convert the generated hprof file into a standard hprof file, and then use MAT to open it.

Then open it with MAT.

Click Histogram. We can see the input leaked object we suspect, "activity"
We can see that there are two instances in our MainActivity. If one of them is suspected to have been leaked, continue to the analysis below and right-click to select list incoming object
You can reference the information of these two activities.

It is already clear that one of our activities is held by sContext. sContext is static and its lifecycle is the same as that of the Application, therefore, the Activity is leaked throughout the Application lifecycle.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.