In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the knowledge of "how to understand Handler memory leak". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
That's the wrong answer.
Some friends see this question and say, is that it? It's too easy.
"the inner class holds a reference to the external class, that is, Hanlder holds a reference to Activity, so it cannot be recycled."
In fact, this answer is wrong, or missing the point.
Memory leak
The algorithm of reachability analysis is used in the Java virtual machine to determine whether the object can be recycled. That is, through the GCRoot object as the starting point, search down the path (reference chain), and if an object or object group is found to be in an unreachable state, it will be recycled.
Memory leak means that some objects (short-period objects) are useless, but are referenced by other useful classes (long-period objects), resulting in useless objects occupying memory space and forming memory leaks.
Therefore, if the above question is only answered that the inner class holds a reference to the outer class and does not indicate who the inner class is referenced, then memory leakage will not occur because both the inner class and the outer class are useless objects. can be recycled normally.
So the key to this question is, who is the inner class referenced? That is, who quoted Handler?
Let's do some practical research together.
Memory leak in Handler 1. Send delay message
In the first case, a delay message is sent over handler:
Class MainActivity: AppCompatActivity () {
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_handler)
Btn.setOnClickListener {
/ / Jump to HandlerActivity
StartActivity (Intent (this, HandlerActivity::class.java))
}
}
}
Class HandlerActivity: AppCompatActivity () {
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_handler2)
/ / send delay message
MHandler.sendEmptyMessageDelayed (0, 20000)
Btn2.setOnClickListener {
Finish ()
}
}
Val mHandler = object: Handler () {
Override fun handleMessage (msg: Message?) {
Super.handleMessage (msg)
Btn2.setText ("2222")
}
}
}
We send a message with a delay of 20 seconds in HandlerActivity. Then finish immediately after opening HandlerActivity. See if there is a memory leak.
View memory leaks and analyze
It's easy to check memory leaks now. AndroidStudio comes with a heap dump (Heap Dump) file for analysis and clearly marks the memory leak points.
When we run the project, click Profiler--Memory, and we can see the following image, a real-time picture of running memory:
Capture heap dump
You can see that there are two buttons in the picture that I marked out:
Capture heap dump file button, that is, generate hprof file, this file will show the use of the Java heap, after clicking this button, AndroidStudio will help us generate the heap dump file and analyze it. GC button, usually before we capture the heap dump file, click GC, we can recycle some weak references to prevent interference to our analysis.
So after we open HandlerActivity, we immediately finish, then click the GC button, and then click the capture heap dump button. AndroidStudio automatically jumps to the following interface:
Analysis heap dump
You can see that there is a Leaks in the upper left corner, which is the point of your memory leak. Click on the class to see the memory leak. In the lower right corner is the reference path to the memory leak class.
From this picture, we can see that there is a memory leak in our HandlerActivity. From the reference path, it is referenced by the instance of the anonymous inner class mHandler, while the reference of Handler is held by Message and the reference of Message is held by MessageQueue.
Combined with what we have learned about Handler and this reference path analysis, the complete reference chain for this memory leak should be:
Main thread-> threadlocal-> Looper-> MessageQueue-> Message-> Handler-> Activity
So the leader of this reference is the main thread, which will certainly not be recycled. As long as the running thread is not recycled by JVM, it will be specially taken care of by JVM like static variables.
The cause of this memory leak has been figured out. Of course, this is not the only case of Handler memory leak. Take a look at the second situation:
2. The child thread has not finished running.
The second example, which we often use, works in child threads, such as requesting the network, and then updates the UI through Handler after the request succeeds.
Class HandlerActivity: AppCompatActivity () {
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_handler2)
/ / running child threads
Thread {
Thread.sleep (20000)
MHandler.sendEmptyMessage (0)
}
Btn2.setOnClickListener {
Finish ()
}
}
Val mHandler = object: Handler () {
Override fun handleMessage (msg: Message?) {
Super.handleMessage (msg)
Btn2.setText ("2222")
}
}
}
After the same run, check for memory leaks:
Child thread memory leak
It can be found that the main reason for the memory leak here is that the running child thread, because the anonymous inner class of the child thread holds a reference to the external class, and the child thread itself is running all the time. As I just said, running threads will not be recycled, so the reference chain of memory leakage here should be:
Running child threads-> Activity
Of course, the Handler here also holds a reference to Activity, but the main cause of memory leakage lies in the child thread itself, that is, the operator thread does not use Handler, but other variables or methods that call Activity will still have a memory leak.
So I think this situation can not be regarded as a memory leak caused by Handler, the root cause is caused by child threads, if the memory leak of child threads is solved, such as stopping child threads when Activity is destroyed, then Activity can be recovered normally, then there is no Handler problem.
Extension question 1: why an inner class holds a reference to an external class
This is because although the inner class and the external class are written in the same file, different class files are generated after compilation, in which an instance of the external class is passed in the constructor of the inner class, and then the members of the external class can be accessed through this$0.
In fact, it is also quite easy to understand, because in the inner class, you can call the methods, variables, and so on of the external class, so you will certainly hold the reference of the external class.
Post a piece of class code that the inner class looks at with JD-GUI after compilation, and you may have a better understanding:
/ / original code
Class InnerClassOutClass {
Class InnerUser {
Private int age = 20
}
}
/ / class code
Class InnerClassOutClass$InnerUser {
Private int age
InnerClassOutClass$InnerUser (InnerClassOutClass var1) {
This.this$0 = var1
This.age = 20
}
}
Is there any difference between the inner classes in the extension question 2:kotlin and Java
In fact, you can see that in the above code, I have added a sentence
Btn2.setText ("2222")
This is because anonymous inner classes in kotlin fall into two situations:
In Kotlin, if the anonymous inner class does not use the object reference of the external class, it will not hold the object reference of the external class. At this time, the anonymous inner class is actually a static anonymous inner class, so there will be no memory leakage. In Kotlin, if an anonymous inner class uses a reference to an external class, like I just used btn2, it will hold a reference to the external class, and memory leaks will need to be considered.
So I specifically added this sentence to let the anonymous inner class hold a reference to the external class to reproduce the memory leak problem.
Similarly, inner classes in kotlin are different from Java:
All inner classes in Kotlin are static by default, that is, static inner classes. If you need to call an external object method, you need to decorate it with inner, change it to the same inner class as Java, and hold a reference to the external class, so you need to consider memory leaks. Resolve memory leaks
After all that has been said, how to solve the memory leak problem? In fact, the solutions to all memory leaks are more or less the same, mainly as follows:
Do not let a long lifecycle object hold a reference to a short lifecycle object, but use a long lifecycle object to hold a reference to a long lifecycle object.
For example, the context passed when using Glide does not use Activity but uses the context of Application instead. There is also singleton mode that does not pass into the Activity context.
Change the strong reference of an object to a weak reference
A strong reference is that after an object is strongly referenced, it will not be recycled anyway.
A weak reference is when garbage collection, if the object is only associated with a weak reference (there is no strong reference associated with him), then the object will be reclaimed.
Soft references are reclaimed when the system is about to experience a memory overflow.
Virtual reference means that an object has no effect on its lifetime at all, and it is impossible to obtain an object instance through a virtual reference, which is rarely used.
Therefore, if we change the object to a weak reference, we can ensure that it will be normally reclaimed during garbage collection, such as the weak reference instance passed into Activity in Handler:
MyHandler (WeakReference (this)) .sendEmptyMessageDelayed (0, 20000)
/ / the inner class in kotlin defaults to static inner class
Class MyHandler (var mActivity: WeakReference): Handler () {
Override fun handleMessage (msg: Message?) {
Super.handleMessage (msg)
MActivity.get ()? .changeBtn ()
}
} Internal classes are written as static classes or external classes
As in the case of Hanlder above, sometimes inner classes are used improperly and are prone to memory leaks, and the solution is to write them as external classes or static inner classes.
Remove areas where memory leaks may occur at the end of a short period
For example, memory leaks caused by Handler delayed messages, resources not closed, collection not cleaned, and so on, can be eliminated when Activity is closed:
@ Override
Protected void onDestroy () {
/ / remove all handler messages
If (mHanlder! = null) {
MHandler.removeCallbacksAndMessages (null)
}
Super.onDestroy ()
This is the end of "how to understand Handler memory leak". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.