当前位置:网站首页>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 .
边栏推荐
- Can molecular modeling solve the data dilemma of AI pharmacy?
- Machine learning practice - integrated learning-23
- Redefinition problem of defining int i variable in C for loop
- A brief introduction to the for loop. Some of the code involves arrays
- Uniapp 应用开机自启插件 Ba-Autoboot
- C# 泛型是什么、泛型缓存、泛型约束
- Cloud native - runtime environment
- 04 pyechars geographic chart (example code + effect diagram)
- AI制药的数据之困,分子建模能解吗?
- How can non-standard automation equipment enterprises do well in product quality management with the help of ERP system?
猜你喜欢
![[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

Summary: golang's ide:vscode usage

Code layered management of interface testing based on RF framework

黑猫带你学eMMC协议第26篇:eMMC的硬件复位操作(H/W reset)

单调栈Monotonic Stack
![[error prone points of C language] Part 4: detailed rules for storing structures in memory](/img/87/555e6db40f23b5bd4919bd7bdce776.png)
[error prone points of C language] Part 4: detailed rules for storing structures in memory

The largest rectangle in leetcode84 histogram

Linear classifier (ccf20200901)

Jetpack Compose 完全脱离 View 系统了吗?

Science 重磅:AI设计蛋白质再获突破,可设计特定功能性蛋白质
随机推荐
SQL most commonly used basic operation syntax
How to open the power saving mode of win11 system computer
How many times can the WordPress user name be changed? Attach the method of changing user name
苏黎世联邦理工学院 | 具有可变形注意Transformer 的基于参考的图像超分辨率(ECCV2022))
A brief introduction to the for loop. Some of the code involves arrays
Black cat takes you to learn EMMC protocol chapter 27: what is EMMC's dynamic capacity?
CTO of youhaoda, MVP of Huawei cloud, and Zhang Shanyou: build cloud native applications based on kubernetes and dapr
LeetCode206 反转链表
leetcode 376. Wiggle Subsequence
Flexpro software: measurement data analysis in production, research and development
Code layered management of interface testing based on RF framework
区块反转(暑假每日一题 7)
快速读入
Sliding Window
Ruan Bonan of Green Alliance Technology: cloud native security from the open source shooting range
Detailed explanation of the usage of C # static
2020-12-13
Connected Block & food chain - (summary of parallel search set)
The largest rectangle in leetcode84 histogram
STM32F103 several special pins are used as ordinary io. Precautions and data loss of backup register 1,2