Java is a type of garbage collection language. Its advantage is that developers do not need to deliberately manage memory allocation, which reduces the possibility that applications will crash due to local segmentation faults, and prevents unfreezing memory from squeezing the stack (heap). Therefore, the code written is safer.
Unfortunately, there are still many logical leaks in Java that are prone to memory leakage. If you are not careful, your Android application can easily waste unfree memory, which will eventually lead to an error throw of memory running out (out-of-memory, OOM).
1. The reason for general memory leak is that when all references to the object have been released, the object has not been released. (Translator's note: Cursor forgot to close, etc.)
2. The reason for logical memory leak is that when the application no longer needs this object, all references to the object have not been released.
If you hold a strong reference to an object, the garbage collector cannot recycle the object in memory.
In Android development, the most likely memory leak problem is Context. For example, the Context of Activity contains a large number of memory references, such as View Hierarchies and other resources. Once a Context is leaked, it also means leaking all objects it points to. Android machines have limited memory, and too many memory leaks can easily lead to OOM.
Detecting logical memory leaks requires subjective judgment, especially the life cycle of the object is not clear. Fortunately, Activity has a clear life cycle and it is easy to find the cause of the leak. Activity.onDestroy() is regarded as the end of the Activity life. Programmatically, it should be destroyed, or the Android system needs to recycle this memory (Translator's note: When the memory is insufficient, Android will recycle invisible activities).
If this method is executed, there is still a strong reference to the activity in the stack, and the garbage collector cannot mark it as recycled memory, and our original purpose is to recycle it!
The result is that the Activity survives outside its life cycle.
Activity is a heavyweight object and should be handled by the Android system. However, logical memory leaks always happen inadvertently. (Translator's note: I once tried an activity that caused a 20M memory leak). In Android, there are only two traps that lead to potential memory leaks:
The static variable of the global process (process-global). This monster that ignores the state of the application and holds a strong reference to the Activity.
Threads that live outside the Activity life cycle. No strong references to Activity were cleared.
Check if you have encountered the following situations.
1.Static Activities
The static Activity variable is defined in the class, and the currently running Activity instance is assigned to this static variable.
If this static variable is not cleared after the end of the Activity life cycle, it will cause a memory leak. Because static variables run through the life cycle of this application, the leaked activities will always exist in the application process and will not be collected by the garbage collector.
static Activity activity; void setStaticActivity() { activity = this;}View saButton = findViewById(R.id.sa_button);saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticActivity(); nextActivity(); }});2.Static Views
Similar situations can happen in singleton mode, and if the Activity is often used, it is practical to save an instance in memory. As mentioned earlier, forcing the life cycle of an Activity is quite dangerous and unnecessary, and it cannot be done anyway.
Special case: If a View initialization consumes a lot of resources and remains unchanged during an Activity life cycle, it can be turned into static and loaded onto the View Hierachy. In this way, when the Activity is destroyed, the resource should be released. (Translator's note: The memory is not released in the sample code. Just null this static view, but it is still not recommended to use the static view method)
static view; void setStaticView() { view = findViewById(R.id.sv_button);}View svButton = findViewById(R.id.sv_button);svButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticView(); nextActivity(); }});3.Inner Classes
Continue, assuming there is an internal class in the Activity, doing so can improve readability and encapsulation. If we create an internal class and hold a reference to a static variable, congratulations, the memory leak is not far from you (Translator's note: empty when destroyed, um).
private static Object inner; void createInnerClass() { class InnerClass { } inner = new InnerClass();}View icButton = findViewById(R.id.ic_button);icButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createInnerClass(); nextActivity(); }});One of the advantages of internal classes is that they can access external classes. Unfortunately, the reason for memory leaks is that the internal classes hold strong references to external class instances.
4.Anonymous Classes
Similarly, anonymous classes also maintain references to external classes. So memory leaks are easy to happen when you define anonymous AsyncTsk in your Activity. When the asynchronous task executes a time-consuming task in the background, the activity is unfortunately destroyed (Translator's note: user exits, system recycling), this Activity instance held by AsyncTask will not be recycled by the garbage collector until the asynchronous task is completed.
void startAsyncTask() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { while(true); } }.execute();} super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);View aicButton = findViewById(R.id.at_button);aicButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAsyncTask(); nextActivity(); }});5.Handler
By the same token, define anonymous Runnable and execute it with anonymous class Handler. The Runnable inner class will hold an implicit reference to the external class and is passed to the MessageQueue of the Handler message queue. The Activity instance will not be destroyed until the Message message is processed, resulting in a memory leak.
void createHandler() { new Handler() { @Override public void handleMessage(Message message) { super.handleMessage(message); } }.postDelayed(new Runnable() { @Override public void run() { while(true); } }, Long.MAX_VALUE >> 1);}View hButton = findViewById(R.id.h_button);hButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createHandler(); nextActivity(); }});6.Threads
We once again show memory leaks through Thread and TimerTask.
void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start();}View tButton = findViewById(R.id.t_button);tButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { spawnThread(); nextActivity(); }});7.TimerTask
As long as it is an instance of an anonymous class, regardless of whether it is in the worker thread or not, it will hold a reference to the Activity, resulting in a memory leak.
void scheduleTimer() { new Timer().schedule(new TimerTask() { @Override public void run() { while(true); } }, Long.MAX_VALUE >> 1);}View ttButton = findViewById(R.id.tt_button);ttButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { scheduleTimer(); nextActivity(); }});8.Sensor Manager
Finally, the system services can be obtained through Context.getSystemService(int name). These services work in their respective processes, helping applications handle background tasks and hardware interactions. If you need to use these services, you can register listeners, which will cause the service to hold a reference to the Context. If these listeners are not logged out when the Activity is destroyed, it will cause memory leaks.
void registerListener() { SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL); sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);}View smButton = findViewById(R.id.sm_button);smButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { registerListener(); nextActivity(); }});Summarize
Having seen so many examples that can lead to memory leaks, it is easy to eat up all the memory of your phone, making garbage collection and processing more frequently, and even in the worst case, it will lead to OOM. Garbage collection operations are expensive and can lead to visible lags. Therefore, pay attention to the held reference chain when instantiating, and often perform memory leak checks.