android memory and memory overflow analysis

  • 2020-05-19 05:43:12
  • OfStack

1. Memory mechanism of Android
Android's programs are written in the Java language, so the memory management of Android is similar to that of Java. The programmer allocates memory for objects through new, and all objects are allocated space in java heap. However, the release of the object is done by the garbage collector. The memory mechanism in C/C++ is "who pollutes, who governs". java is more humanized and we have a special cleaner (GC).
So how can GC verify that an object has been discarded? Java USES the principle of directed graphs. Java considers reference relationships as directed edges of graphs, which point from the referrer to the reference object. The thread object can be used as the starting vertex of the directed graph, which is a tree starting from the starting vertex. All the objects that can be reached by the root vertex are valid objects, and GC will not recycle these objects. If an object (connected subgraph) is not reachable from the root vertex (note that the graph is a directed graph), then we assume that this object is no longer referenced and can be recycled by GC.
2. Memory overflow of Android
How did the memory overflow in Android occur?
The virtual machine of Android is register-based Dalvik, which has a maximum heap size of 16M, or 24M. Therefore, the amount of memory space we can use is limited. The OutOfMemory error occurs if our memory footprint exceeds a fixed 1 level.
Why do you run out of memory? I think there are two main reasons:
The & # 8226; Due to the mistake of our program, we kept the reference of some resources (such as Context) for a long time, which caused the memory leak and the resource could not be released.
The & # 8226; Saving multiple objects (such as Bitmap) that consume too much memory, resulting in memory overruns.
3. The evil static
static is a keyword in Java that, when used to modify a member variable, belongs to the class, not to an instance of the class. So variables modified with static, which have a long lifetime, should be treated with caution if they are used to reference instances that are too resource-intensive (Context at its best).

public class ClassName {  
     private static Context mContext;  
     // omit   
}  

The code above is dangerous if you assign Activity to mContext. So even if the Activity has onDestroy, the Activity will not be released because there is still an object to hold a reference to it.
Let's take an example from the Android document.

private static Drawable sBackground;  
 @Override  
 protected void onCreate(Bundle state) {  
   super.onCreate(state);  
   TextView label = new TextView(this);  
   label.setText("Leaks are bad");  
   if (sBackground == null) {  
     sBackground = getDrawable(R.drawable.large_bitmap);  
   }  
   label.setBackgroundDrawable(sBackground);  
   setContentView(label);  
 }  

sBackground is a static variable, but we found that we did not explicitly save the reference of Contex. However, when Drawable is connected to View, Drawable sets View to a callback. Since View contains the reference of Context, we still save the reference of Context. The chain of references is as follows:
Drawable- > TextView- > Context
So, finally, the Context was not released, and a memory leak occurred.
How can this reference be effectively avoided?
First, you should try to avoid using the static member variable to reference too many instances of the resource, such as Context.
2. Context should try to use Application Context, because Application's Context has a long life cycle, so it will not cause memory leak problems.
3. Use WeakReference instead of strong references. For example, you can use WeakReference < Context > mContextRef;
You can also refer to the Article section of the Android documentation for details on this section.
4. It's all about threads
Threads are also an important source of memory leaks. The main reason for thread memory leak is the uncontrollable thread life cycle. Consider the following piece of code.

public class MyActivity extends Activity {  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        new MyThread().start();  
    }  
    private class MyThread extends Thread{  
        @Override  
        public void run() {  
            super.run();  
            //do somthing  
        }  
    }  
}  

This code is very common and simple, and is the form we use most of the time. Let's consider a problem: suppose the run function of MyThread is a time-consuming operation. When we start this thread, we will change the horizontal screen of the device into portrait screen. In general, Activity will be recreated when the screen is transformed.
Since our thread is an inner class of Activity, a reference of Activity is saved in MyThread. When MyThread's run function does not end, MyThread will not be destroyed, so the old Activity it refers to will not be destroyed, so the memory leak problem will occur.
Some people like to use Android AsyncTask offers, but in fact AsyncTask problem is more serious, Thread only at the end of the run function does not appear this kind of memory leak problem, however, is to use ThreadPoolExcutor AsyncTask internal implementation mechanism, the class of Thread object lifecycle is uncertain, is out of the control of the application, so if AsyncTask as Activity inner classes, are more prone to the problem of memory leaks.
How to solve the memory leak problem caused by this thread?
1. Change the inner class of a thread to a static inner class.
2. Save the Context reference with a weak reference inside the thread.
The solution model is as follows:

public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget> extends  
        AsyncTask<Params, Progress, Result> {  
    protected WeakReference<WeakTarget> mTarget;  
    public WeakAsyncTask(WeakTarget target) {  
        mTarget = new WeakReference<WeakTarget>(target);  
    }  
    @Override  
    protected final void onPreExecute() {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            this.onPreExecute(target);  
        }  
    }  
    @Override  
    protected final Result doInBackground(Params... params) {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            return this.doInBackground(target, params);  
        } else {  
            return null;  
        }  
    }  
    @Override  
    protected final void onPostExecute(Result result) {  
        final WeakTarget target = mTarget.get();  
        if (target != null) {  
            this.onPostExecute(target, result);  
        }  
    }  
    protected void onPreExecute(WeakTarget target) {  
        // No default action  
    }  
    protected abstract Result doInBackground(WeakTarget target, Params... params);  
    protected void onPostExecute(WeakTarget target, Result result) {  
        // No default action  
    }  
}  

In fact, the threading problem is not just about memory leaks, but also about catastrophic problems. Since this article is about memory, I won't discuss it here.

Related articles: