instantiation
An object is an instance of a class. The process of creating an object is also called instantiation of a class. Objects are created using classes as templates. For example, Car Car = new Car(); , we Create a new class instance of Car.
Some object instantiation requires other object instances, such as ImageView instantiation requires Context objects, that is, ImageView holds a reference to Context.
Directed graph
A graph marked with A directed line segment on each edge is called A directed graph. In Java, garbage Collection adopts the way of directed graph for memory management, and the direction of the arrow represents the reference relationship. For example, B ← A requires A in B, and B references A.
In the directed graph D={S,R}, for Si,Sj belongs to S. If there is any path from Si to Sj, it can be said that Si can reach Sj. That is, when B ← A breaks, it is said to be unreachable, and then A is unreachable to B.
Shallow heap
Represents the memory consumed by the current object
Represents the sum of the memory consumed by the current object plus the memory it references
The garbage collection
Java Garbage Collection (GC) is one of the main differences between Java and C++/C. As Java developers, they generally do not need to write special memory Collection and Garbage cleaning code, and they do not need to be afraid of memory leakage and overflow problems like C programmers. This is because there are automatic memory management and garbage cleaning mechanisms in Java virtual machines. Generally speaking, this mechanism marks the memory in a VM and determines which memory needs to be reclaimed. According to a certain reclamation policy, the system automatically reclaims the memory to ensure that the memory space in the VM is Nerver stopped to prevent memory leaks and overflow.
In the case of the GC, when a programmer creates an object, the GC starts monitoring the address, size, and usage of the object. Typically, GC records and manages all objects in the heap in a directed graph, which determines which objects are “reachable” and which are “unreachable”. When an object is unreachable, that is, when it is no longer referenced, it is garbage collected.
There is a lot of documentation on the web about reachable relationships, as shown in figure 6. In line 6, O2 changes direction and Obj2 no longer refers to Main, i.e. they are unreachable and Obj2 may be reclaimed in the next GC.
What is a memory leak
When you no longer need an instance, the object is still referenced, preventing from being bargage collected. This situation is called a Memory Leak.
In Android, memory leaks are often caused by different life cycles. Here are a few common reasons:
public class MainActivity extends AppCompatActivity {
private final Handler mHandler2 = new Handler() {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
Copy the code
The above problem is actually a memory overflow caused by a non-static inner class holding an external class, which we will discuss below.
The IDE prompt is shown below:
Here’s how it works: When an Android application starts, the framework creates a Looper object for the main thread of the application. The Looper object contains a simple Message Queue and can loop through messages in the Queue. These messages include most application Framework events, such as Activity lifecycle method calls, button clicks, and so on, which are added to the message queue and processed individually. The Looper object for the main thread will accompany the entire life of the application.
When we instantiate a Handler object in the main thread, it is automatically associated with the main thread Looper’s message queue. Any Message sent to the Message queue will have a reference to Handler, and if the current Activity has been terminated/destroyed, the Handler, since it is a non-static inner class, will hold the object of the outer class and hold on to the current Activity object. At this point, memory leaks are highly likely.
The following situations also cause Handler memory leaks
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */}}, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}
Copy the code
This program is very simple, we can imagine that it should start and then shut down, but really shut down?
Anyone with common sense can see that it sends a Message that will run ten minutes later, meaning that Message will be kept referenced for ten minutes, which is a memory leak of at least ten minutes.
The final correct code is as follows:
public class MainActivity extends AppCompatActivity { private final MyHandler mHandler=new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.postDelayed(sRunable,10000); finish(); } /** * The inner class of an anonymous/non-static class keeps a reference to the Activity in which it is located. Private static final Runnable sRunable=new Runnable() {@override public void run() {}}; Private Static Class MyHandler extends Handler{private static Class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{public static class MyHandler extends Handler{ Private Final WeakReference<MainActivity> mActivity; // If an instance is not needed but still referenced, it is called a memory leak. private MyHandler(MainActivity activity) { mActivity =new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { MainActivity activity=mActivity.get(); if(activity! =null){ super.handleMessage(msg); }}}}
Copy the code
The related concept of WeakRefrence: the existence of a weak reference object does not prevent the object it points to from being collected by the garbage collector. The most common use of weak references is to implement canonicalizing mappings, such as hash tables. ‘Suppose the garbage collector decided at some point that an object was weakly reachable (that is, all the weak references pointing to it now are weak references), then the garbage collector would clear all weak references pointing to it, and then the garbage collector would mark the weakly reachable object as finalizable, So they can be recycled later. At the same time or later, the garbage collector places the newly cleared weak references in the Reference Queue that was registered when the weak-reference object was created.
-
A StrongReference is the most commonly used reference. If an object has a strong reference, the garbage collector will never collect it. When running out of memory, the Java virtual machine would rather throw outofMemoryErrors to abort the program than randomly recycle objects with strong references to resolve the memory problem.
-
If an object has only soft references, there is enough memory for the garbage collector to not reclaim it. If you run out of memory, the objects are reclaimed. As long as the garbage collector does not collect it, the object can be used by the program. Soft references can be used to implement memory-sensitive caching (examples are given below). A soft reference can be used in conjunction with a ReferenceQueue (ReferenceQueue), and if the object referenced by the soft reference is collected by the garbage collector, the Java virtual machine adds the soft reference to the ReferenceQueue associated with it.
-
Weak references The difference between weak references and soft references is that objects with only weak references have a shorter life cycle. When the garbage collector thread scans the memory area under its control, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient. However, because the garbage collector is a low-priority thread, objects that have only weak references are not necessarily found quickly. Weak references can be used in conjunction with a ReferenceQueue (ReferenceQueue), and if the object referenced by a weak reference is garbage collected, the Java virtual machine adds the weak reference to the ReferenceQueue associated with it.
-
A virtual reference, as its name implies, is a virtual reference. Unlike other references, a virtual reference does not determine the life cycle of an object. If an object holds only virtual references, it can be collected by the garbage collector at any time, just as if there were no references at all. Virtual references are mainly used to track the activity of objects being collected by the garbage collector. One difference between a virtual reference and a soft or weak reference is that a virtual reference must be used in conjunction with a ReferenceQueue. When the garbage collector is about to reclaim an object and finds that it has a virtual reference, it adds the virtual reference to the reference queue associated with it before reclaiming the object’s memory.
Static variables cause memory leaks
public class SecondActivity extends AppCompatActivity{ private static Context sContext; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); sContext = this; findViewById(R.id.finish).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); }}); }}
Copy the code
Memory leaks are also possible because static variables hold the current Activity. Causes the static variable to still hold its reference when the current Activity ends. A deeper dive into the life cycle of static variables in Android is necessary. See the Life Cycle of Android Static Variables. This article is very clear and highly recommended.
public class AppManager { private static AppManager sAppManager; private Context context; private AppManager(Context context) { this.context=context; } public static AppManager getInstance(Context context) { if(sAppManager==null){ sAppManager=new AppManager(context); } return sAppManager; }}
Copy the code
Memory overflow detection is as follows:
When creating the above singleton, since a Context is passed in, the lifetime of the Context is critical:
1. If the Application Context is: OK, this is OK, because the singleton life cycle is the same as the Application Context.
2. If it is an Activity Context: when the Activity to which this Context corresponds exits, its memory is not reclaimed because the singleton holds a reference to the Activity.
The solution is as follows:
public class AppManager { private static AppManager sAppManager; private Context context; Private AppManager(Context Context) {/** * To prevent memory leaks * so that whatever Context is passed will end up using the Application Context, * and the singleton lifetime is the same as the Application's, Thus preventing the memory leak * / this. The context = context. GetApplicationContext (); } public static AppManager getInstance(Context context) { if(sAppManager==null){ sAppManager=new AppManager(context); } return sAppManager; }}
Copy the code
Non-static inner classes hold instances of external classes
public class ThirdActivity extends AppCompatActivity {
private static MySample sMySample=null;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
if(sMySample==null){
sMySample=new MySample();
}
}
class MySample{
}
}
Copy the code
The code above results from LeakCanary:
Because we create a singleton of a non-static inner class inside the Activity and use that singleton’s data every time we start the Activity (to avoid duplicate resource creation), this writing causes a memory leak, again because the non-static inner class holds an external class object. Instead, make the inner class static or extract it into a singleton. If Context is required, use ApplicationContext.
Memory leak caused by thread
Runnable is an anonymous inner class (if AsyncTask has an anonymous inner class) that has an implicit reference to the current Activity. If the task is not completed before the current Activity is destroyed, the Activity’s memory resources cannot be reclaimed, resulting in a memory leak. Example code is as follows:
public class FourthActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fourth); sample(); finish(); } private void sample() { new MyThread().start(); } private class MyThread extends Thread { @Override public void run() { while (true) { SystemClock.sleep(1000); }}}}
Copy the code
Not surprisingly, we soon received notification of a memory leak. To do this, use a static inner class, as shown below:
private static class MyThread extends Thread { @Override public void run() { while (true) { SystemClock.sleep(1000); }}}
Copy the code
Property animations have a type of wireless loop animation. If you play this animation in the current Activity and don’t stop it at the end (onDestory), the animation will continue even though you can’t see the animation in action. However, the View of the Activity will be held by the animation, and the View will hold the current Activity, so the Activity cannot be released. The code is as follows:
public class FifthActivity extends AppCompatActivity { private View view; private ObjectAnimator mAnimator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fifth); view = findViewById(R.id.view); MAnimator = ObjectAnimator. OfFloat (view, "translationX", 0400); mAnimator.setDuration(1000); mAnimator.setRepeatCount(ValueAnimator.INFINITE); mAnimator.start(); }}
Copy the code
The solution is simple: unanimate OnDestory().
@Override protected void onDestroy() {
super.onDestroy();
if(mAnimator!=null){
mAnimator.cancel();
}
}
Copy the code
Memory leak caused by Dialog
It is also easy to leak memory by not demolish the current Dialgo before the Activity to which the current Dialog is attached is destroyed.
In the build. The gradle:
Dependencies {debugCompile 'com. Squareup. Leakcanary: leakcanary - android: 1.5' releaseCompile 'com. Squareup. Leakcanary: leakcanary - android - no - op: 1.5' testCompile 'com. Squareup. Leakcanary: leakcanary - android - no - op: 1.5'}
Copy the code
In Application: public class ExampleApplication extends Application {
@Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... }}
Copy the code
Don’t forget to register for the Application!
When you debug your App, if you find that memory is tight. LeakCanary will automatically alert you with a notification. Easy to go. Of course, there are many ways to LeakCanary, but for more details, please go to the LeakCanary homepage.
Refer to the article
Android memory leak analysis
Common memory leak scenarios and solutions
“8 Common Problems causing APP Memory Leaks”
Android memory leak case and analysis
Exploring the Art of Android Development