What is a memory leak
What is memory leak? In layman's terms, some objects in the heap will no longer be used, but the garbage collector cannot clear them from memory.
Memory leaks are serious problems because it blocks memory resources and reduces system performance over time. If the processing is not done effectively, the final result will cause the application to exhaust memory resources and fail to serve normally, causing the program to crash and throw an exception.
There are usually two types of objects in heap memory: referenced objects and unreferenced objects. The referenced object is an object that still has an active reference in the application, while the unreferenced object does not have any active references.
The garbage collector will recycle objects that are not referenced, but will not recycle objects that are still being referenced. This is also the source of memory leaks.
What operations can cause memory leaks
Below we introduce several common memory leaks
1. Unexpected declaration of global variables is the most common and easiest memory leak problem, such as:
function fn() { name = 'Zhang San'; }
When the interpreter explains the above function, it will treat name as a global variable, that is, = 'Zhang San'. As long as the window object is not cleaned, the name attribute and attribute value will always exist, causing memory leakage.
Solution:
(1) Just add the var, let or const keywords before the variable declaration, so that the variable will leave the scope after the function is executed.
(2) Use this keyword
function fn() { = 'Zhang San'; }
(3) You can add "use strict" at the beginning of a JavaScript file and use strict mode. This way, parsing JavaScript in strict mode can prevent unexpected global variables
(4) After use, assign it to null or reassign it
2. Leakage caused by timer
let name = 'Zhang San'; setInterval(() => { (name); }, 100);
In the above code, as long as the timer is running, the name referenced in the callback function will always occupy memory.
3. Closure, console log, loop (a loop will be generated when two objects are referenced and retained to each other). Let’s look at an example of internal training leakage caused by JavaScript closures.
let fun = function() { let name = 'Zhang San'; return function() { return name; }; };
Calling fun() will cause the memory allocated to name to be leaked. After the above code is executed, an internal closure is created. As long as the returned function exists, the name cannot be cleaned because the closure is always referring to it.
Common memory leak issues
1. The resource object is not closed
Resource objects (such as Cursor, File, and other Closeable objects), they often use buffers, and buffers are not only in the JVM, but also outside the JVM. If you just set variables to null without closing them, the buffer will not be released, which will often lead to memory leakage.
Solution: Generally, close the resource object in finally, and then set the object to null
2. The registered object is not cancelled
In the subscriber mode, if the registered object is no longer used and is not logged out in time, it will cause the subscriber list to maintain the reference of this object, prevent garbage collection, and lead to memory leakage. Common scenarios: Dynamically register BroadcastReceiver, register PhoneStateListener, register EventBus, etc.
There are also cases where you use the subscriber mode in custom.
Solution: Generally deregister in onDestroy()
3. Static instances of non-static inner classes
Non-static inner classes hold references to external class instances. If an instance of a non-static inner class is static, it will have the entire life cycle of the app's survival period, and hold references to external class for a long time, preventing external class instances from being recycled.
It is very common to use internal classes, especially anonymous internal classes: some interface anonymous implementation classes are internal classes.
Solution: (1) Change to a static inner class and no longer hold references to external class instances (2) Avoid declaring static instances of non-static inner class (3) Extract the inner class and encapsulate it into a singleton. If Context is needed, use Application Context without special requirements; if Activity Context is needed, empty it after use, or use weak references
4. Memory leak caused by singleton mode
Due to the static nature of the singleton pattern, its life cycle is as long as our application. If a singleton holds unlimited strong references to the Activity, it will lead to memory leaks
Solution: Use weak references to Activity, or use Application Context when there are no special requirements
Temporary memory leak
The non-static Handler holds a reference to the Activity or Service, and the target in the Message points to the Handler instance, so when the Message is queued in the MessageQueue and not processed for a long time, the Activity edge will not be recycled, resulting in temporary memory leakage.
Solution: (1) Use a static Handler inner class and then use a weak reference to the object (Activity or Service) held by the Handler (2) Remove the message in the message queue in onDestroy() (null)
Similar: AsyncTask also has a Handler mechanism, and there is also the same temporary memory leak risk
6. Memory leaks caused by not cleaning up the objects in the container in time
Container classes generally have a long life cycle. If objects that are no longer used internally are not cleaned up in time, the internal object edges will always be referenced by the container class. The subscriber list in the above 2 also belongs to the container class. In addition, common container classes include thread pools, object pools, image cache pools, etc. If a thread in the thread pool has a ThreadLocal object, because the thread object is always used in a loop, the ThreadLocal object will be referenced all the time. Pay attention to emptying and releasing the value object.
7. Static View causes memory leak
Sometimes, when an Activity is often started, but the corresponding View is time-consuming to read, we can maintain the rootView reference to the Activity through static View variables. This way, you don't have to read and render the View every time you start the Activity. This is indeed a great way to increase the speed of your Activity startup! But be aware that once the View is attached to our Window, it will hold a reference to the Context (i.e. Activity). And our View has a static variable, so the Activity is not recycled. Solution: When using static View, you need to ensure that the static View is detached during resource recycle.
8. The attribute animation is not closed in time, resulting in memory leakage
When using ValueAnimator or ObjectAnimator, if you do not cancel the animation in time, memory leakage may occur. Because in the cancel method, endAnimation(); is called in the endAnimation, there is a singleton of AnimationHandler that holds a reference to the attribute animation object
Solution: Call the cancel method of the animation when in onDestory
Memory leak
Currently, there are great compatibility issues in the implementation of WebView in Android. Google supports various ROM manufacturers to customize their own WebView implementations. There are large differences between each ROM, and most of them have memory leaks. In addition to calling its internal clearCache(), clearHistory(), removeAllViews(), freeMemory(), destroy() and null, the general rough and effective solution is: put the Activity containing the WebView in a separate process, and destroy the process if it is not needed, thereby freeing all the memory occupied.
10. Other system controls and custom Views
Using AlertDialog before Android Lollipop may cause memory leaks
If there are threads or animations in the view, you should stop it in time. This is to prevent memory leakage, and can be ended in the onDetachedFromWindow method. The time for this method callback is called when the View's Activity exits or the current View is removed. This is a good time to end the animation or thread. There is also a corresponding method. The time for the onAttachedToWindow method is called when the Activity containing the View is started. Callback The callback is before the onDraw method.
11. Other common causes of memory leaks
- (1) When constructing Adapter, no cached contentView is used
- (2) Bitmap does not use recycle() to release memory when it is not used
- (3) Beware of memory leakage caused by the thread's failure to terminate; for example, if a Thread with a life cycle exceeds the Activity, remember to end the thread when exiting the Activity. A typical example is that the run method of HandlerThread is a dead loop, which will not end on its own. The life cycle of the thread exceeds the life cycle of the Activity. We must manually call().quit() in the destroy method of the Activity to not leak.
- (4) Avoid memory leakage caused by errors in code design patterns; for example, if A holds B, B holds C, C holds A, no one can release such a design.
At the end of the article
To understand the harm of memory leaks, let’s give a simple example. There is a hotel with 100 rooms. Customers always check in at the front desk and then get the room key. If some customers do not need the room and do not return the keys, over time, fewer and fewer available rooms at the front desk will be available, and their income will be reduced, and they will be on the verge of bankruptcy. When the program applies for memory but does not return it, over time, the available memory will decrease and less, and the OS will protect itself and kill the process. This is what we often call OOM (out of memory).
This is the article about the in-depth investigation of the causes of Android memory leaks. For more related content on Android memory leaks, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!