当前位置:网站首页>Analyzing while experimenting - memory leakage caused by non static inner classes

Analyzing while experimenting - memory leakage caused by non static inner classes

2022-06-11 05:12:00 Kakar

Experiment and analysis - Memory leakage caused by non static inner classes


about Java For the programmer , Memory leakage must be a common problem in the development process , There are many situations that can lead to memory leaks , The fundamental problem is Java Memory reclamation manager for (GC) It is impossible to recycle objects that are not in use , This causes the object to persist in memory , The resulting memory leak , The end result is a memory overflow .

stay Android in , because Android The structure and ecology of , The problem has also become relatively easy to attract people's attention , You must often listen to experienced people Android Development colleagues say this sentence :“ Non static inner classes hold references to outer classes , Attention should be paid to memory leakage when using ”.
I heard that , We should analyze it by ourselves , How to understand ? Why? ? And doubt its correctness , After all, only in this way can I improve , So we take this as a breakthrough , Let's analyze this sentence first .

First, how to understand this sentence , There are several key words in this sentence , The static 、 Inner class 、 hold 、 External class ,
From non static and inner classes, we know that we must define a non static inner class , Consider the following code

public class MainActivity2 extends AppCompatActivity {
    
	private MessageCall messageCall;
	
	@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    	super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    	messageCall = new MessageCall();
    	findViewById(R.id.doSomething).setOnClickListener(view -> {
    
            messageCall.doSomething();
        });
    }

	class MessageCall {
    
		public void doSomething(){
    
			System.out.println("doSomething");
		}
	}
}

For the convenience of checking the memory leakage , We use leakcanary To monitor our memory , Run the program , Click button , Then press the back button , Return to desktop , You'll find that , No memory leaks ,WHY? The static , The inner classes all meet .
So does it hold references to external classes ? Let's take a look Bytecode You can see ( It can be downloaded from Build -> Analyze APK -> Choose the appropriate dex -> Find... Under the package name MainActivity2 Of Class -> Right click Show Bytecode see )
 Insert picture description here

You can see onCreate In the method, we new-instance 了 MainActivity2$ MessageCall, And in init When it came to our MainActivity2, See here we might as well have a look MainActivity2$MessageCall This class
 Insert picture description here
there this$0 In fact, that is MainActivity2, therefore MessageCall Hold on to MainActivity2 References to .
That's again WHY?WHY? Will not cause memory leaks ?

In fact, non static inner classes hold references to outer classes , It is not an absolute condition that memory leaks will occur , There is another condition , That is, the life cycle of the inner class is longer than that of the outer class , Memory leakage will occur .

Let's try it in the just doSomething In the method , Add a thread , Let it run in the background 20 second

class MessageCall {
    

        public void doSomething(){
    
            Runnable work = new Runnable() {
    
                @Override public void run() {
    
                    System.out.println("doSomething start");
                    SystemClock.sleep(20000);
                    System.out.println("doSomething end");
                }
            };
            new Thread(work).start();
            
        }
    }

Let's run the program again , Click button , Then press the back button , Return to desktop , This time, there was a memory leak
 Insert picture description here
It can be seen that ,MainActivity2 destory, however MessageCall Of this$0 Leakage and MessageCall It's because MessageCall$1.this$1 The real source of leakage is Thread-4, You can check the bytecode by yourself , You can find Thread In fact, it also holds an external class MessageCall Referenced by , So it led to a chain reaction ,Thread- -> MessageCall -> MainActivity2.

that , What can we do to solve this problem ? There is a very simple solution , It must be known to all , That is to use static inner classes , That's all right. , In this way, the life cycle of the inner class is not directly related to the life cycle of the outer class , When the outer class destory When , Static inner classes are not affected , Naturally, there is no memory leak .

We analyze and solve the problem here , In fact, those who have done so will certainly find that there are still some problems in the actual operation ,
The most important problem is using static inner classes , You can't directly reference some member variables of an external class , This makes the code feel complicated , that , What else can we do ?

In fact, in this case , There is also a very clever technique in it , That's using lambda, Let's take a look at this method , Change the code

class MessageCall {
    

        public void doSomething(){
    
            Runnable work = () -> {
    
                System.out.println("doSomething start");
                SystemClock.sleep(20000);
                System.out.println("doSomething end");
            };
            new Thread(work).start();

        }
    }

Run the program and you will find , No memory overflow occurred
 Insert picture description here
Let's look at what happens to bytecode ? We see directly MainActivity2$MessageCall Bytecode in , It mainly depends on doSomething Method
 Insert picture description here
You can see here that there will be lambda, Let's see lambda

 Insert picture description here
Found that its internal call MessageCall A static method in is called lambda$doSomething, go back to MessageCall In bytecode , Found this static method , You must know the reason when you see here , Because this is a static method , So it will not lead to holding MessageCall References to , This will not lead to MessageCall Held in MainActivity References to will not be released , Played a series of chain effects , This is it. lambda The magic of .

But don't be happy too soon , Because it doesn't mean lambda Just a matter of , All we use is lambda One of the , be called Non-instance-capturing lambdas, That means we are lambda No external instance is used in the method , Contrary , Of course, there is another , Namely Instance-capturing lambdas, That is, kidnapping an external instance lambda, Let's see , If we were MainActivity It defines a int The value of the type is 0, And then in run Of lambda In the thread, let this value be re assigned to 1

private Integer i = 0;
class MessageCall {
    

        public void doSomething(){
    
            Runnable work = () -> {
    
                System.out.println("doSomething start");
                i = 1;
                SystemClock.sleep(20000);
                System.out.println("doSomething end");
            };
            new Thread(work).start();
        }
    }

So let's run it again , Will find , Still memory overflow , So let's look at bytecode , After the previous analysis , We directly located MessageCall In bytecode doSomething Method
 Insert picture description here
It is not found that static 了 , therefore , Of course, it will be similar to what we didn't use before lambda It's written the same way , out of memory .

In conclusion , On the premise that the life cycle of the inner class is longer than that of the outer class :
1. Don't use lambda, Non static class , Methods do not use external instances , Memory leaks can result
2. Don't use lambda, Non static class , Use external instances inside methods , Memory leaks can result
3. Use lambda, Non static class , Methods do not use external instances , No memory leaks
4. Use lambda, Non static class , Use external instances inside methods , Memory leaks can result
5. Static class , Will not cause memory overflow

Such a conclusion , Are you right lambda It's a step closer to understanding .

原网站

版权声明
本文为[Kakar]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203020541351081.html