1 Java 内部类 内存泄漏
public class JavaActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java);
View btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
doWork();
}
});
}
void doWork() {
Runnable work = new Runnable() {
public void run() {
try {
sleep(2000000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
new Thread(work).start();
}
}
上面的代码,点击按钮后,关闭 Activity后,会发生内存泄漏(这不废话吗......) 看一下生成的文件,生成3个文件,2个内部类生成2个文件(也就是N个内部类生成N文件....)
通过生成的字节码我们可以明确看到以下结论
内部类会持有外部类的引用,通过构造方法传递 我们在通过字节码来确认一下吧,可以看到红色部分,持有外部类引用
总结
泄漏根本原因: 内部类持有外部类引用
解决:静态内部类,弱引用持有
2 Java Lambda
public class JavaLambdaActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java_lambda);
View btn = findViewById(R.id.btn);
btn.setOnClickListener(v -> doWork());
}
void doWork() {
Runnable work = () -> {
try {
sleep(2000000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
new Thread(work).start();
}
}
和上面的JavaActivity 完全一样,只不过写法变成lambda表示,会发生什么?
在看一下字节码
1 Java代码里面,使用Lambda表达式可以减少内部类文件的生成
2 而每个class文件,被加载到虚拟机后都会在方法去保留类信息,而class本身也有独立的常量池等
3 而在使用时在一个class查询快,还是多个class里面查询快?
结论:推荐使用Lambda替代之前写法
3 Kotlin 内部类 (与外部类无关联)
class KotlinActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin)
val button = findViewById<View>(R.id.btn)
button.setOnClickListener { doWork() }
}
private fun doWork() {
val work = Runnable {
sleep(2000000)
}
Thread(work).start()
}
}
还是和和上面的JavaActivity 完全一样,只不过语言是kotlin了,那这样会内存泄漏吗?
答案:不会(因为此时内部类没有引用任何内部类信息)
- app\build\tmp\kotlin-classes\debug 目录下 执行命令
和Java lambda 是不是很像(kotlin没有doWork 这部分字节码)
- 那为什么kotlin 不会内存泄漏了?
通过上面smail,很容易得出结论
1 kotlin 内部类没有与外部类关联的时候,使用的是 invoke-static
2 所以 ,不会发生内存泄
4 Kotlin 内部类 (与外部类有关联)
class KotlinActivity : BaseActivity() {
private var test = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin)
val button = findViewById<View>(R.id.btn)
button.setOnClickListener { doWork() }
}
private fun doWork() {
val work = Runnable {
test = 110
test()
sleep(2000000)
}
Thread(work).start()
}
fun test() {
}
}
结论
1 kotlin 内部类与外部类关联的时候,会发生内存泄了
2 看上面的smail文件,左侧有关联,右侧无关联
5 Java Lambda VS Kotlin
我们apk包中,发现Java Lambda写法和kotlin 内部类写法完全一样,都是生成2个文件
对比一下2个字节码
kotlin没有doWork 字节码
对比一下2个smail文件
onClick 方法的时候 Java是invoke-virtual 而kotlin invoke-static
这2个文件几乎完全一致
结论
Java Lambda 会发生内存泄泄漏吗??
只有Java Lambda显式调用外部类时才会强引用