从编译角度看Kotlin内存优化

移动开发
今天我们来聊一聊由JetBrains开发的一种用于现代多平台应用的静态编程语言——Kotlin。

作者|闫永俊,单位:中国移动智慧家庭运营中心

​Labs 导读

今天我们来聊一聊由JetBrains开发的一种用于现代多平台应用的静态编程语言——Kotlin。

Kotlin可以被编译为Java字节码,也可以被编译成JavaScript,方便在没有JVM的设备上运行。除此之外,Kotlin还可以被编译成二进制代码直接运行在机器上。

在Google I/O2017中,Google宣布在Android上为Kotlin提供一等支持。目前,Kotlin已经成为Android应用开发的首选语言。

Kotlin相对于Java来说,有很多优点,如空安全、更加易用的Lambda表达式、支持扩展、众多的语法糖等。但是较少有人提及Kotlin的从编译角度上对Java做的内存优化,这里我们通过反编译的方法略窥一二。

Part 01  Java内部类持有外部类的引用

Java中有一个普遍的认知,Java中内部类会持有外部类的引用,使用不当就容易造成内存泄漏。参看下面例子。

我们编写如下代码来验证。

我们先创建一个父类,用于观察子类是否会调用finalize方法。​

public class BaseActivity extends AppCompatActivity {
@Override
protected void finalize() throws Throwable {
Log.e("yanlog", "BaseActivity finalize:" + this);
super.finalize();
}
}

我们创建一个子类,子类中创建一个不会终止的Thread。​

public class TmpJavaActivity extends BaseActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}

通过测试会发现,BaseActivity的finalize方法始终无法被调用,即外部类TmpJavaActivity始终无法被回收。我们使用反编译工具jadx来观察下反编译后的smail代码。


通过上述反编译后的smail代码可以看到,Java会将外部类对象作为参数传递给内部类对象,一旦内部类无法释放,会造成外部类一直无法被释放。从而造成内存泄漏。

Part 02  kotlin内部类非必要不持有外部类引用

上述同样的代码,我们使用Kotlin写一遍,如下所示:​

class TmpActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tmp)
val thread = Thread {
while (true) {
Thread.sleep(1000)
}
}
thread.start()
}
}

我们通过观察日志发现,外部类TmpActivity可以被正常回收。下面直接看下smail源码。

图片

从上述smail源码可以看到,与Java语言不同,Kotlin中的内部类会被编译成一个普通的类。因为内部类实际运行不依赖外部类,所以编译后,不会将外部类作为内部类构造方法的参数传递给内部类,即该内部类不会持有外部类的应用,所以不会造成内存泄漏。

但是如果内部类实际需要持有外部类引用呢?我们来观察如下代码​

class TmpActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tmp)
val thread = Thread {
while (true) {
Log.e("yanlog","thread"+this@TmpActivity)
java.lang.Thread.sleep(1000)
}
}
thread.start()
}
}

观察反编译后的smail源码如下

图片

通过上述源码可以看到,在构造内部类的对象时,会将外部类的引用传递给内部类,从而造成内存泄漏。

通过上述两个例子可以看到。Kotlin语言中,内部类非必要不会持有外部类的引用,较Java而言,减少了内存泄漏的场景。​

责任编辑:未丽燕 来源: 移动Labs
相关推荐

2020-02-04 09:53:05

数据安全数据泄漏信息安全

2019-04-28 16:10:50

设计Redux前端

2015-05-05 11:04:31

CoreOS自动化运维

2013-09-16 16:01:23

Android开发代码

2021-01-06 09:47:51

内存Go语言

2019-11-27 10:11:22

勒索病毒网络安全

2020-11-19 10:09:55

漏洞逆向角度证书覆盖

2017-09-06 15:54:14

2012-04-29 10:37:28

APP

2010-07-16 09:00:20

开源RedOffice红旗2000

2022-02-17 08:16:23

MMU内存管理

2018-07-26 07:21:12

2009-07-08 19:44:56

2021-10-14 08:58:48

Java冒泡排序

2013-12-11 21:48:38

OpenStack

2014-07-14 15:19:43

IT信息工程运维

2017-11-20 16:17:50

智慧城市

2024-01-19 09:21:35

携程开源

2010-06-07 10:44:09

2021-04-28 22:42:36

SaaS软件技术
点赞
收藏

51CTO技术栈公众号