当前位置:网站首页>Analysis while experiment - a little optimization of memory leakage in kotlin

Analysis while experiment - a little optimization of memory leakage in kotlin

2022-06-11 05:13:00 Kakar

Experiment and analysis - Kotlin A bit of optimization for memory leakage in

We've analyzed JAVA The non - static inner class of C , Take a look at the same Kotlin Performance in , Similarly, we still have experience from analysis Android Development colleagues say this sentence :“ Non static inner classes hold references to outer classes , Attention should be paid to memory leakage when using ” Start .

First of all, we know that in Kotlin in , There are nested classes , Inner classes and anonymous inner classes , Before analyzing non static inner classes ,
Let's first look at anonymous inner classes , Anonymous inner classes in JAVA Anonymous instance objects can be directly passed in , And in the Kotlin in , Need to pass through object Keyword to create

class KLeakActivity : Activity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Thread(object : Runnable{
    
            override fun run() {
    
                println("doSomething start")
            }
        }).start()
    }
}

Let's look at bytecode :
 Insert picture description here
It can be seen that ,Kotlin Anonymous inner classes do not hold external references by default , This and Java Different , This is exactly Kotlin Do the optimization of the .
Next , If you write like this , The compiler will prompt you to use lambda Instead of writing in this way , So we changed it to lambda Take a look at

class KLeakActivity : Activity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Thread {
     println("doSomething start") }.start()
    }
}

Look at the bytecode and see what the difference is :
 Insert picture description here
There is a sget-object operation , Let's see $KLeakActivity$onCreate$1
 Insert picture description here
You can see , This is actually a static construction method , also , Does not hold a reference to an external class , Here will be INSTANCE Value assignment , So on the top sget-object Operation to get the static value , And force it into Runnable object , Because it realizes run Method , Therefore, it is strongly transformed and then transmitted to Thread, Finally, call it. start Method execution .

Kotlin Of lambda and Java There are some differences in bytecode ,Java Will become a way to call static methods , and Kotlin A static instance object will be created to call .
in addition , If you read the previous article, you should remember ,Java lambda There is a distinction between lambda Using external instances and not using external instances in 2 Kind of , stay Kotlin It's the same in , If you change the code to the following

var i = 0
override fun onCreate(savedInstanceState: Bundle?) {
    
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    Thread {
     i = 1 }.start()
}

From the bytecode ,$KLeakActivity$onCreate$1 There is no static constructor , In the default construction, the KLeakActivity As a parameter init, Hold on to KLeakActivity example , therefore , This writing method also has the hidden danger of memory leakage .

After knowing the knowledge of anonymous inner classes , Let's move on to the inner class , If you want to use inner classes , and Java There will be a difference
Kotlin Inner classes in need to use inner Keywords to mark , And it holds references to external classes , amount to JAVA Non static inner classes in , Let's take a look

class KLeakActivity : Activity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val messageCall = MessageCall()
        removeWechatBtn.setOnClickListener {
    
            messageCall.doSomething()
        }
    }

    inner class MessageCall {
    
        fun doSomething(){
    
            Thread {
    
                println("doSomething start")
                SystemClock.sleep(20000)
                println("doSomething end")
            }.start()
        }
    }
}

We use it directly lambda To write doSomething Method , Because we know from the knowledge of anonymous inner classes above , Write lambda And don't write lambda Kotlin Will give us the corresponding optimization , This optimization does not hold references to external classes by default , So we don't need to consider lambda He Fei lambda The difference between the

Next , Let's look at bytecode
 Insert picture description here
At present Activity oncreate When , initialization KLeakActivity$onCreate$1, That's our OnClickListener object , Next , Because we use inner classes , Into KLeakActivity$MessageCall Instance of to OnClickListener object , And then look KLeakActivity$onCreate$1 Bytecode

 Insert picture description here
Called KLeakActivity$MessageCall Inside doSomething Method , Until then messageCall Bytecode

 Insert picture description here
here , The same operation as the anonymous inner class we analyzed earlier , So I can see , The final doSomething Method does not affect memory leaks , and messageCall Inner classes are indeed held , But not by KLeakActivity$MessageCall$doSomething$1 hold , So it will not lead to its release

And when we modify the code

class KLeakActivity : Activity() {
    
	var i = 0
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val messageCall = MessageCall()
        removeWechatBtn.setOnClickListener {
    
            messageCall.doSomething()
        }
    }

    inner class MessageCall {
    
        fun doSomething(){
    
            Thread {
    
                println("doSomething start")
                i = 1
                SystemClock.sleep(20000)
                println("doSomething end")
            }.start()
        }
    }
}

In this case , It can lead to KLeakActivity$MessageCall$doSomething$1 Hold on to MessageCall example , and MessageCall The instance holds KLeakActivity example , Finally, the memory leak occurs .

Next, let's look at nested classes , Nested classes are equivalent to static inner classes , So naturally, it will not lead to memory leakage , You can analyze and view the bytecode for verification .

class KLeakActivity : Activity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val messageCall = MessageCall()
        messageCall.doSomething()
    }

    inner class MessageCall {
    
        fun doSomething(){
    
            Thread {
    
                println("doSomething start")
                SystemClock.sleep(20000)
                println("doSomething end")
            }.start()
        }
    }
}

 Insert picture description here
You can see , It does not hold references to external classes ,doSomething The routine in the method is the same as before , It will become a static object for static calls, so it will not lead to memory leakage .

In conclusion :
1. Nested class : No memory leaks
2. Inner class : The use of external instances within class methods can lead to memory leaks , Not using an external instance within a class method will not cause a memory leak
3. Anonymous inner class : The use of external instances within class methods can lead to memory leaks , Not using an external instance within a class method will not cause a memory leak

原网站

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