投稿者:
発行:2020年7月20日
TL;DR: 現在のKotlin/Nativeの自動メモリ管理の実装には並行性の点で限界があり、代替案を調査中です。既存のコードは引き続き動作し、サポートされます。引き続き全文をお読みください。
歴史を少し
Kotlin/Nativeは、Kotlinとネイティブプラットフォーム固有の環境をスムーズに統合するためのソリューションとして設計されています。基本的に、そのビジョンは、JVM言語にとってのKotlin/JVMと同じように、C互換言語にとってのKotlin/Nativeになることです。Kotlin/NativeがObjective-Cエコシステムをサポートすることで、モバイルアプリ間でKotlinコードを効率的に共有し、すべてのベンダーのAPIを自然かつ正確に使用することが可能になります。ソリューションでは実現が難しいか不可能でしょう。
2016年にKotlin/Nativeプロジェクトが立ち上げられたとき、メモリ管理ソリューションを設計する必要がありました。Objective-Cの相互運用性がテーブルの上にあったため、参照カウントを使用した自動メモリ管理アプローチに多くの考察と注意が払われました。最もシームレスにフィットするという利点を提供する、モデルを正確に模倣したアプローチの実験も行われました。しかし、Objective-Cのエコシステムでは、ループを持つオブジェクト・グラフはランタイムによって自動的に管理されません。プログラマーは循環参照を識別し、ソースコードに特別な方法でマークしなければなりません。このアプローチを試した後、それは開発をより楽しくするというKotlinの基本理念と強く相反するという結論にすぐに達しました。Kotlinは、安全性のためにそのような精度が必要な場合は開発者により明示的であることを求めますが、そのような余分なコードが言語によって管理できる単なるサンプルテンプレートである場合は求めません。それでも、参照カウントメモリーマネージャーはKotlin/Nativeプロジェクトを開始するのに十分書きやすく、トライアルアンドデリートアルゴリズムに基づく周期的なゴミコレクターは、Kotlinプログラマーが期待する開発体験を提供するために追加されました。
Kotlin/Nativeプロジェクトが成熟し、より広く採用されるようになると、この自動参照カウントベースのメモリ管理スキームの限界が明らかになり始めます。まず、メモリ割り当てを多用するアプリケーションで高いスループットを得ることは困難です。しかし、性能は重要ですが、Kotlinの設計における唯一の要素ではありません。
しかし、マルチスレッドと同時実行をミックスに入れると、制限はより厳しくなります。完全に自動化された参照カウントメモリ管理をうまく使用しているすべてのエコシステムでは、同時実行は「グローバルインタープリタロック」メカニズムのようなものによって厳しく制限されています。Kotlinの場合、このアプローチはオプションではありません。モバイルアプリケーションでは、メインスレッドと並行して実行されるバックグラウンドスレッドにCPU負荷の高い処理をオフロードする機能が不可欠です。
現在のアプローチは
この問題を解決するため、Kotlin/Native用に、シングルスレッド・コードを効率的に実行し、スレッド間でデータを共有できるようにする独自の制約セットが開発されました。まず、オブジェクト・グラフが変更されないように凍結されなければならないという要件が追加されました。そうして初めて、他のスレッドと共有できるようになります。さらに、元のスレッドでオブジェクト・グラフへの参照が保持されていない場合、オブジェクト・グラフは切り離されたオブジェクト・グラフとして別のスレッドに完全に転送することができます。おまけに、「ミュータブルな状態の共有」という恐ろしい問題は、イミュータブルなデータだけを共有することで、ほとんどの場合回避できます。この解決策はしばらくの間うまくいきました。
コンセプト的には魅力的ですが、Kotlin/Nativeのメモリ管理に対する現在のアプローチには多くの欠陥があり、それが普及を妨げています。モバイル開発者は、スレッド間でオブジェクトを自由に共有できることに慣れており、その際にデータ競合を回避するための手法やアーキテクチャパターンを開発してきました。多くのアーリーアダプターが実証しているように、メインスレッドをブロックしない効率的なアプリケーションをKotlin/Nativeを使って書くことは可能ですが、その能力には険しい学習曲線が伴います。
Kotlinにはもう1つの側面があります。目標は、異なるプラットフォーム間でKotlinコードを共有できるようにすることです。しかし、いくつかの並行コードは、最初は安全でレースフリーであっても、Kotlin/JVMとKotlin/Nativeの間で共有することはほとんど不可能です。特に、さまざまな並行データ構造と同期プリミティブは、汎用的でありドメイン固有である可能性がありますが、この2つの間で共有するのが難しいことが判明しました。
Kotlin/Nativeにマルチスレッドのkotlinx.coroutineを実装しようとすると、特に難しい問題が発生します。同期プリミティブは内部で変更可能な状態を共有する必要があり、Kotlin/Nativeでは特別なアトミック参照によってサポートされています。しかし、既存のメモリ管理アルゴリズムは、このような参照を通じてループを追跡しません。多くの作業を行った後でも、特定の同時実行シナリオではメモリー・リークに悩まされ、明確な解決策がありません。
新しいKotlin/Nativeメモリ・マネージャー
これらの問題に対処するため、Kotlin/Nativeのオブジェクト共有の制限を解除し、未使用のKotlinメモリをすべて自動的に追跡して再生成し、パフォーマンスを向上させ、安全で開発者の特別な管理やアノテーションを必要としない、完全にリークフリーの並行プログラミングプリミティブを提供する、Kotlin/Native用の代替メモリマネージャの開発に着手しました。特別な管理やアノテーションを必要としません。新しいメモリマネージャーは、コンパイルされたバイナリ全体で使用されます。既存のコードとほぼ互換性のある形で導入する予定ですので、現在動作しているコードは引き続き動作します。とはいえ、レースフリーのデータ共有のための安全なメカニズムとしてオブジェクトの凍結を引き続きサポートする計画であり、Kotlin/NativeだけでなくKotlin言語全体でKotlinが不変データを扱う方法を改善する方法を検討する予定です。メモリ管理に関連する既存のアノテーションは、古いコードが有効なままであることを保証するために、新しいメモリマネージャで適切な動作をするようになります。一方、既存のメモリマネージャのサポートは継続され、Kotlin/Native用のマルチスレッドライブラリもリリースされるため、それらの上でアプリケーションを開発することができます。
今後、プロジェクトが発展し、様々な設計上の決定がなされるにつれて、より詳細な情報が共有される予定です。お楽しみに!




