目录

Kotlin/Native

最近 KMP 或者说 KMM 在移动端挺火,我非常好奇它是如何跨端的

看了一下官方文档,只知道是通过 LLVM 把 Kotlin 转为 IR 再转为目标平台二进制。这部分我目前还没去分析,因为 LLVM 相关的知识我实在知之甚少,后面也会去补足

但是,我渐渐发现了另一个点:在我们写 kotlin 代码的时候,我们并不会显式执行内存释放的操作(比如 c/c++ 会显示调用 free/delete,比如 rust 有 drop trait)。那么,将 kotlin 代码转换为二进制的时候,如何处理这种内存释放的问题?

那我们肯定猜到是有垃圾回收的逻辑的。要么引用计数,要么就是一个现代的垃圾回收器。

如果引用计数,需要在生成代码的时候维护一个引用指针

如果是垃圾回收器,那么不单单需要一个垃圾回收器,还要知道所有的内存分配情况,所以还需要一个 heap 的概念,作为内存的大管家。

Kotlin Native Memory Management

看了 kotlin 官方文档是说,一开始使用引用计数来做内存回收,但是无法处理循环引用,而且似乎线程之间的内存共享也有问题(具体不太清楚,并不是第一批的 KMM 使用者)?总之,最终实现了一个类似 jvm 的垃圾回收器,使用的 cms

那 cms 理论上是存在 stop the world 的,kotlin 官方文档 也提及了类似的问题。抱着去验证文档内容的心态,创建了一个 kmm demo,于是发现了如下的线程:

/img/in-post/kotlin_native_thread.jpg

/img/in-post/cms.jpg

所以,除了发现有 main gc thread(后续看了下 cms 在 mark 阶段也可能是多线程的) 以外,还有一个 gc timer thread

为了搞清楚 gc timer thread,我去寻找了 kotlin native 的源码

然后被一个命名问题困扰了一天,最后终于发现了问题的原因,也是一个非常搞笑的事情

源码目录

我们可以直接找到 kotlin 工程中的 kotlin native 目录,然后其中很显眼的有个 runtime,显然就是运行时的必要组件,这里当然也包含了 gc 相关的逻辑

于是我们会到 gc 目录下是如下的样子:

/img/in-post/kotlin_native_gc_tree.jpg

惊奇的发现,其实存在多种 gc 方式,这也体现了 kotlin native 其实还在不断完善中,并算不上是一个完美的方案

同时,也找到了我们关注的 gc scheduler。但是很不幸,gc scheduler 也有多种

因为各自方案均有多种,这对于我阅读代码增加了一些困扰。幸运的是,经过一段时间的翻阅,我大概理清了逻辑。我们只需要关注 cms 和 adaptive scheduler 即可

adaptive scheduler 即是我们 gc timer thread(也是现在 kotlin 默认使用的)

/img/in-post/kotlin_native_gc_scheduler.jpg

逻辑也较为好懂,gc timer thread 每隔固定时间回来判断当前是否需要 gc。

同时,如果发生了内存分配操作,allocator 会通知到 gc scheduler(setAllocationBytes),然后根据 memory boundary 来判定是否要执行 gc。而 gc(cms) 中又有很多细节,包括 barrier, parallel mark 等等,这部分理论知识欠缺,有点没看明白

好奇心害死猫

其实,上面得到的这些论证,已经满足了我的基本好奇心了。无论是 LLVM 还是一个 CMS 算法,对于目前的我来说,都很难看得懂。

但是,我一不小心发现了如下代码,导致自己浪费了一天

/img/in-post/kotlin_native_default_gc.jpg

无论是 kmm demo 中 debugger 展示的线程名,还是 kotlin 官方文档中的介绍,都是说使用的 cms,为啥到这里变成了这个 parallel_mark_concurrent_sweep?

还记得上面提到的源码目录么,这个东西的目录名字叫做 pmcs,并不是 cms,而且里面的代码就叫做 ParallelMarkConcurrentSweep.cpp,而且翻阅 konan config 发现,还真有 pmcs

于是,彻底懵逼了,难道新版本的 kotlin 使用了 pmcs,kotlin 文档没来得及更新?反查自己的 kotlin 版本 1.9.20,已经非常新了,查阅 1.9.20 分支代码,默认仍然是 pmcs,但是 debugger 确确实实显示的是 cms···

浪费了一天以后发现,目前 pmcs 和 cms 好像是一个东西···对比了一下代码也非常类似

同时,看了一下历史提交,发现了一个非常搞笑的事情

/img/in-post/kotlin_native_commit_history.jpg

/img/in-post/kotlin_native_commit_history_2.jpg

这次提交,将默认的 cms 修改为了 pmcs,但是 native library 用的依然还是 cms.bc

我们再看一下编译的 module

/img/in-post/kotlin_native_compile_module.jpg

发现 cms.bc 就是对应的 gc/cms/cpp 的代码

所以,现在并不存在所谓的 pmcs 都是 cms,而 cms 的代码 和 pmcs 的代码基本一致

哈哈一笑,浪费了一天

总结

经过查阅 gc 这块内容,发现 kotlin/native 目前并算不上成熟,所以还是继续观望吧