作者|颜永军,单位:中国移动智能家居运营中心实验室介绍今天我们就来聊一聊JetBrains为现代多平台应用开发的静态编程语言——Kotlin。Kotlin可以编译为Java字节码或JavaScript以在没有JVM的设备上运行。此外,Kotlin还可以编译成二进制代码直接在机器上运行。在GoogleI/O2017上,Google宣布在Android上对Kotlin提供一流的支持。目前,Kotlin已经成为Android应用开发的首选语言。与Java相比,Kotlin有空安全、更易用的Lambda表达式、支持扩展、语法糖众多等优点。但是很少有人从编译的角度提到Kotlin对Java的内存优化。这里我们通过反编译一窥究竟。Part01Java内部类持有对外部类的引用在Java中有一个共同的理解,即Java中的内部类持有对外部类的引用,使用不当很容易造成内存泄漏。请参见下面的示例。我们编写如下代码来验证。我们先创建一个父类,观察子类是否会调用finalize方法。publicclassBaseActivityextendsAppCompatActivity{@Overrideprotectedvoidfinalize()throwsThrowable{Log.e("yanlog","BaseActivityfinalize:"+this);super.finalize();}}我们创建一个子类,在子类中创建一个不会终止的Thread。公共类TmpJavaActivity扩展BaseActivity{@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){super.onCreate(savedInstanceState);线程thread=newThread(newRunnable(){@Overridepublicvoidrun(){while(true){try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}}});thread.start();}}通过测试,会发现BaseActivity的finalize方法永远无法调用,也就是外部类TmpJavaActivity永远无法被回收。我们使用反编译工具jadx来观察反编译后的smail代码。从上面反编译的smail代码可以看出,Java会将外部类对象作为参数传递给内部类对象。一旦内部类不能被释放,外部类就永远不会被释放。从而造成内存泄漏。Part02Kotlin内部类不是必需的,也没有持有外部类引用上面相同的代码,我们用Kotlin重新写一遍,如下:super.onCreate(savedInstanceState)setContentView(R.layout.activity_tmp)valthread=Thread{while(true){Thread.sleep(1000)}}thread.start()}}我们通过观察日志发现,外部类TmpActivity可以正常回收。我们直接看smail源码。从上面的smail源码可以看出,与Java语言不同的是,Kotlin中的内部类会被编译成一个普通类。因为内部类的实际运行不依赖于外部类,编译后外部类不会作为内部类构造方法的参数传递给内部类,即内部类不会持有外部类的应用,所以不会造成内存泄漏。但是,如果内部类实际上需要持有对外部类的引用怎么办?让我们观察以下代码classTmpActivity:BaseActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_tmp)valthread=Thread{while(true){Log.e("yanlog","thread"+this@TmpActivity)java.lang.Thread.sleep(1000)}}thread.start()}}观察反编译后的smail源码如下通过上面的源码可以看出insidethestructure使用类的对象时,将外部类的引用传递给内部类,造成内存泄漏。从上面两个例子可以看出。在Kotlin语言中,除非必要,否则内部类不会持有对外部类的引用,这与Java相比减少了内存泄漏的情况。
