Kotlin 内存泄漏初探
写在前面:
最近在学习熟悉号称Android第一语言的--Kotlin,作为一名“资深”Java啰嗦繁琐的“受害者”,第一次感受到了语法糖带来的好处。省去了Java bean类的重复繁琐,回调函数采用了跟javaScript类似的方式,节省了大量代码,逻辑结构也更加的清晰明了,整个项目下来对比使用java下来,可节约至少1/3的代码,并且与Java无缝对接,遇到实在跳不过去的坑,直接换成Java,整个过程毫无违和感。毕业设计打算正式使用Kotlin语言作为开发项目。
来说说在使用Kotlin开发中遇到的坑(与Java不一样的地方),在公司实习(自学),研究内存泄漏的检测在实际中的使用,这里一般推荐LeakCanary,相比官方MAT,采用傻瓜式直接通知栏提醒的方式,使用方便,效率也更高,不熟悉的同学请点Android内存优化(六)LeakCanary使用详解 - CSDN博客。这里我发现了一种比较高效的方式,使用安卓自带的模拟器(模拟器对比真机可调节内存大小,GC线程回收得也更频繁)用LeakCanary配合官方MAT性能检测工具使用(如图1),红色圈圈按钮可直接唤起jvm虚拟机gc线程进行回收,仅仅唤起线程,并不是马上回收,因为gc线程优先级比较低,而且对象需要两次标记才会被回收。
发现问题:
因为使用Kotlin进行开发,自然测试也是使用Kotlin语言。众所周知,在安卓开发中,一个最为简单普遍内存泄漏的例子就是使用Thread或Handler时,使用了非静态内部类,因为在Java中,内部类会默认持有外部对象的引用,从而造成外部对象销毁时,因内部对象还持有其引用导致外部类引用链没有断开,java虚拟机无法正常将其回收。然而在像Java一样使用内部类时发现,并没有发生内存泄漏。
找出原因:
原来,在Kotlin中,内部类可分为嵌套类,内部类和匿名内部类,至于他们的区别与使用,请移步至:Kotlin基础 7 - 内部类 - CSDN博客,虽然同样支持嵌套内部类,不过和Java不一样,Kotlin内部不会默认包含一个指向外部类对象的引用,因此:
1.Kotlin中所有内部类默认是静态的(即嵌套类,不使用inner修饰),能减少很多内存泄漏的问题。(如图2)
2.而内部类(使用inner修饰),对象会隐式持有外部类的对象(跟java一样),引用外部对象的时候只需this@Outter 进行引用即可。使用的时候需要注意内存泄漏的问题。(如图3)
3.对于匿名内部类,则比较特殊,分为两种情况:
第一种,没有使用对外部类对象的引用时(this@Outter),并不会持有外部类对象,此时匿名内部类为静态匿名内部类。(如图4)
第二种,如果使用了对外部类的引用,比如,在匿名内部类中使用了Context(this@Outter),此时,匿名内部类会持有外部类的引用,需要考虑内存泄漏的问题。(如图5)
结论:
1.嵌套类(默认为静态内部类)->不会隐式持有外部类对象,不需要考虑内存泄漏问题。
2.内部类(使用inner修饰)->隐式持有外部类对象,需要考虑内存泄漏问题。
3.匿名内部类:1)代码内部引用了外部对象(this@Outter)->隐式持有外部类对象,需要考虑内存泄漏问题。
2) 代码内部没有引用了外部对象->不会隐式持有外部类对象,不需要考虑内存泄漏问题。