Android中Kotlin函数后续调用更快的原因及调整方法
问题描述
在Android环境中,我用Kotlin和C++实现了相同的冒泡排序算法,传入一致的输入数据对比两者性能差异,发现了如下现象:
- C++函数连续5次调用的执行时间基本稳定:
for length=100000 : 43031; for length=100000 : 43023; for length=100000 : 43046; for length=100000 : 43053; for length=100000 : 43014; - Kotlin函数第一次执行时间远高于后续几次,且后续时间保持稳定,直到APP重启才会恢复第一次的慢速度:
for length=100000 : 41578; for length=100000 : 16331; for length=100000 : 16351; for length=100000 : 16328; for length=100000 : 16325;
每次测试时,两个函数都会接收由Kotlin内置generateSequence函数生成的新IntArray实例(生成算法一致,保证输入可重复)。Kotlin的冒泡排序代码如下:
fun ktBubbleSort(numbers: IntArray): IntArray { for (pass in 0 until (numbers.size - 1)) { // A single pass of bubble sort for (currentPosition in 0 until (numbers.size - pass - 1)) { // This is a single step if (numbers[currentPosition] > numbers[currentPosition + 1]) { val tmp = numbers[currentPosition] numbers[currentPosition] = numbers[currentPosition + 1] numbers[currentPosition + 1] = tmp } } } return numbers; }
请问:
- 这种Kotlin函数第一次执行慢、后续执行快的现象原因是什么?
- 能否在不重启APP的情况下改变这种行为?
原因分析与解决方案
一、现象原因
这是ART虚拟机的JIT(即时编译)机制导致的:
- Kotlin代码运行在Android的ART虚拟机上,第一次执行
ktBubbleSort时,虚拟机采用解释模式执行,未做任何优化,因此速度较慢。 - 当函数被多次调用后,ART的JIT编译器会识别这是高频调用的热点函数,将其编译为本地机器码,后续调用直接执行编译后的机器码,速度大幅提升。
- 而C++代码在编译阶段就已生成机器码,运行时无需再做动态优化,因此每次执行时间都保持稳定。
另外,ART的JIT编译结果会被缓存,直到APP进程销毁(重启)才会失效,所以后续调用一直保持高速,直到重启APP。
二、不重启APP改变行为的方法
可以通过以下方式让Kotlin函数回到第一次执行的慢状态:
- 触发代码更新(热修复场景):如果APP支持动态代码更新,替换
ktBubbleSort函数的实现,新的函数第一次执行会重新走解释模式。 - 清除ART编译缓存(测试用):通过ADB命令强制清除应用的JIT缓存:
执行该命令后,ART会清除该应用的编译缓存,再次调用adb shell cmd package compile -f --reset your.package.namektBubbleSort时会重新以解释模式执行,恢复第一次的慢速度。 - 自定义类加载器卸载类(复杂场景):如果
ktBubbleSort所在的类通过自定义类加载器加载,可以卸载该类后重新加载,此时函数会重新被解释执行。这种方式需要复杂的类加载逻辑,仅适用于特定测试场景。
内容的提问来源于stack exchange,提问作者polar0010
相关产品推荐
相关产品推荐

