当前位置:网站首页>How does kotlin help you avoid memory leaks?
How does kotlin help you avoid memory leaks?
2022-07-28 13:02:00 【Serbian uncle】
First , The code for this article is located in **https://github.com/marcosholgado/performance-test/tree/kotlin-mem-leak** Medium kotlin-mem-leak On the branch . I created one that would lead to memory leaks Activity, Then observe its use Java and Kotlin Test the performance at writing time . among Java The code is as follows :
public class LeakActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
View button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncWork();
}
});
}
@SuppressLint("StaticFieldLeak")
void startAsyncWork() {
Runnable work = new Runnable() {
@Override public void run() {
SystemClock.sleep(20000);
}
};
new Thread(work).start();
}
}
As shown in the above code , our button After clicking , Performed a time-consuming task . So if we are 20s Close within LeakActivity Memory leaks will occur , Because this new thread holds the right LeakActivity References to . If we are 20s Then close this Activity Words , There will be no memory leak . Then we change this code to Kotlin edition :
class KLeakActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_leak)
button.setOnClickListener { startAsyncWork() }
}
private fun startAsyncWork() {
val work = Runnable { SystemClock.sleep(20000) }
Thread(work).start()
}
}
What do you think , It's like it's just Runable Use in lambda The expression replaces the original template code . And then I use leakcanary And my own @LeakTest The comment writes a memory leak test case .
class LeakTest {
@get:Rule
var mainActivityActivityTestRule = ActivityTestRule(KLeakActivity::class.java)
@Test
@LeakTest
fun testLeaks() {
onView(withId(R.id.button)).perform(click())
}
}
We use this use case for Java Written LeakActivity and Kotlin Written KLeakActivity To test . The test result is Java There is a memory leak when writing , and Kotlin There is no memory leak when writing . This problem bothered me for a long time , Once close to autistic ..

And then one day , I had a flash of inspiration , I think it should have something to do with the compiled bytecode .
analysis LeakActivity.java Bytecode
Java The bytecode generated by class is as follows :
.method startAsyncWork()V
.registers 3
.annotation build Landroid/annotation/SuppressLint;
value = {
"StaticFieldLeak"
}
.end annotation
.line 29
new-instance v0, Lcom/marcosholgado/performancetest/LeakActivity$2;
invoke-direct {v0, p0}, Lcom/marcosholgado/performancetest/LeakActivity$2;-><init>
(Lcom/marcosholgado/performancetest/LeakActivity;)V
.line 34
.local v0, "work":Ljava/lang/Runnable;
new-instance v1, Ljava/lang/Thread;
invoke-direct {v1, v0}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
invoke-virtual {v1}, Ljava/lang/Thread;->start()V
.line 35
return-void
.end method
We know that anonymous inner classes hold references to outer classes , It is this reference that causes the memory leak , Next, we'll find this reference in the bytecode .
new-instance v0, Lcom/marcosholgado/performancetest/LeakActivity$2;
The meaning of the above bytecode is : First we created a LeakActivity$2 Example ..
It's strange that we didn't create this class , Then this class should be automatically generated by the system , What is its function ? We turn on LeakActivity$2 Look at the bytecode of
.class Lcom/marcosholgado/performancetest/LeakActivity$2;
.super Ljava/lang/Object;
.source "LeakActivity.java"
# interfaces
.implements Ljava/lang/Runnable;
# instance fields
.field final synthetic this$0:Lcom/marcosholgado/performancetest/LeakActivity;
# direct methods
.method constructor <init>(Lcom/marcosholgado/performancetest/LeakActivity;)V
.registers 2
.param p1, "this$0" # Lcom/marcosholgado/performancetest/LeakActivity;
.line 29
iput-object p1, p0, Lcom/marcosholgado/performancetest/LeakActivity$2;
->this$0:Lcom/marcosholgado/performancetest/LeakActivity;
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
The first interesting thing is this LeakActivity$2 Realized Runnable Interface .
This means that LeakActivity$2 It's the one who holds LeakActivity Object refers to an object of an anonymous inner class .
# interfaces
.implements Ljava/lang/Runnable;
As we said before , This LeakActivity$2 Should hold LeakActivity References to , Then let's keep looking for .
# instance fields
.field final synthetic
this$0:Lcom/marcosholgado/performancetest/LeakActivity;
Sure enough , We found an external class LeakActivity References to objects of . When was this reference passed in ? It's only possible that it was passed in the constructor , Then let's keep looking for its constructor .
.method constructor
<init>(Lcom/marcosholgado/performancetest/LeakActivity;)V
Sure enough , Passed in the constructor LeakActivity References to objects . Let's go back to LeakActivity In the bytecode of , Look at this. LeakActivity$2 When it is initialized .
new-instance v0, Lcom/marcosholgado/performancetest/LeakActivity$2;
invoke-direct {v0, p0},
Lcom/marcosholgado/performancetest/LeakActivity$2;-><init>
(Lcom/marcosholgado/performancetest/LeakActivity;)V
You can see , We use LeakActivity Object to initialize LeakActivity$2 object , This explains why LeakActivity.java There will be memory leaks .
analysis KLeakActivity.kt Bytecode
KLeakActivity.kt We focus on startAsyncWork The bytecode of this method , Because other parts and Java It's the same way , Only this part is different . The bytecode of this method is as follows :
.method private final startAsyncWork()V
.registers 3
.line 20
sget-object v0,
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
->INSTANCE:Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
check-cast v0, Ljava/lang/Runnable;
.line 24
.local v0, "work":Ljava/lang/Runnable;
new-instance v1, Ljava/lang/Thread;
invoke-direct {v1, v0}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
invoke-virtual {v1}, Ljava/lang/Thread;->start()V
.line 25
return-void
.end method
It can be seen that , And Java Initialize a bytecode containing Activity Implementation of reference Runnable The difference between interface objects is , This bytecode uses static variables to execute static methods .
sget-object v0,
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1; ->
INSTANCE:Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
Let's go deeper KLeakActivity\$startAsyncWork\$work$1 Look at the bytecode of :
.class final Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
.super Ljava/lang/Object;
.source "KLeakActivity.kt"
# interfaces
.implements Ljava/lang/Runnable;
.method static constructor <clinit>()V
.registers 1
new-instance v0,
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
invoke-direct {v0},
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;-><init>()V
sput-object v0,
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
->INSTANCE:Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
return-void
.end method
.method constructor <init>()V
.registers 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
It can be seen that ,KLeakActivity\$startAsyncWork\$work$1 Realized Runnable Interface , But it has static methods , Therefore, there is no need for a reference to an external class object . therefore Kotlin The reason why there is no memory leak comes out , stay Kotlin in , We use lambda( It's actually a SAM) Instead of Java Anonymous inner class in . No, Activity Object, there will be no memory leak . Of course, it's not just Kotlin It has this function , If you use Java8 Medium lambda Words , There will be no memory leakage . If you want to know more about this part , See this article **Translation of Lambda Expressions**. If you need to translate, you can say it in the comments .

Now let's talk about the more important part :
In the above paragraph Lamdba Expressions can be considered static methods . Because they don't use instance properties in the class , For example, using super、this Or member variables in this class . We put this Lambda be called Non-instance-capturing lambdas( I think it's better not to translate here ). And those that need instance properties Lambda It is called a instance-capturing lambdas.
Non-instance-capturing lambdas Can be thought of as private、static Method .instance-capturing lambdas Can be considered ordinary private、instance Method .
What does this passage mean in our article ?
Because we Kotlin Medium lambda Instance properties are not used , So it's a non-instance-capturing lambda, It can be seen as a static method , There will be no memory leak .
If we add a reference to an external class object attribute , This lambda It turns into instance-capturing lambdas, There will be a memory leak .
class KLeakActivity : Activity() {
private var test: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_leak)
button.setOnClickListener { startAsyncWork() }
}
private fun startAsyncWork() {
val work = Runnable {
test = 1 // comment this line to pass the test
SystemClock.sleep(20000)
}
Thread(work).start()
}
}
As shown in the above code , We used test This instance property , Will cause memory leaks . startAsyncWork The bytecode of the method is as follows :
.method private final startAsyncWork()V
.registers 3
.line 20
new-instance v0, Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
invoke-direct {v0, p0},
Lcom/marcosholgado/performancetest/KLeakActivity$startAsyncWork$work$1;
-><init>(Lcom/marcosholgado/performancetest/KLeakActivity;)V
check-cast v0, Ljava/lang/Runnable;
.line 24
.local v0, "work":Ljava/lang/Runnable;
new-instance v1, Ljava/lang/Thread;
invoke-direct {v1, v0}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
invoke-virtual {v1}, Ljava/lang/Thread;->start()V
.line 25
return-void
.end method
Obviously , We introduced KLeakActivity The object of , Therefore, it will lead to memory leakage .
边栏推荐
- What if win11 cannot recognize Ethernet
- Analysis of Andriod low on memory printing principle
- Monotonic stack
- [graduation design] oscilloscope design and Implementation Based on STM32 - single chip microcomputer Internet of things
- 试用copilot过程中问题解决
- Summary: idea problem record
- How many times can the WordPress user name be changed? Attach the method of changing user name
- STM32F103 several special pins are used as ordinary io. Precautions and data loss of backup register 1,2
- 【嵌入式C基础】第2篇:进制转换与BCD编码
- QT signal and slot mechanism (detailed)
猜你喜欢

What if the right button of win11 start menu doesn't respond

05 pyechars basic chart (example code + effect diagram)

Fundamentals of machine learning Bayesian analysis-14

如何在 TiDB Cloud 上使用 Databricks 进行数据分析 | TiDB Cloud 使用指南

Comments are not allowed in JSON

How can non-standard automation equipment enterprises do well in product quality management with the help of ERP system?

2020-12-07
![[embedded explanation] key scanning based on finite state machine and stm32](/img/ce/cc3f959a4e4f5b22e2c711ea887ad7.png)
[embedded explanation] key scanning based on finite state machine and stm32

【嵌入式C基础】第6篇:超详细的常用的输入输出函数讲解

Leetcode 1518. wine change
随机推荐
机器学习实战-决策树-22
Analysis of Andriod low on memory printing principle
Black cat takes you to learn EMMC protocol chapter 27: what is EMMC's dynamic capacity?
机器学习实战-逻辑回归-19
Which big model is better? Openbmb releases bmlist to give you the answer!
How to open the power saving mode of win11 system computer
MySQL is always installed unsuccessfully. Just do it like this
Low code: reduce technical capability requirements and improve software development efficiency
Fundamentals of machine learning - principal component analysis pca-16
归并排序
Android工程师,如何使用Kotlin提供生产力?
MySQL limit paging optimization
04 pyechars geographic chart (example code + effect diagram)
机器学习基础-贝叶斯分析-14
线性分类器(CCF20200901)
[base] what is the optimization of optimization performance?
Quick read in
Rolling update strategy of deployment.
机器学习基础-支持向量机 SVM-17
mysql limit 分页优化