迁移到新版内存管理器
This guide compares the new Kotlin/Native memory manager with the legacy one and describes how to migrate your projects.
The most noticeable change in the new memory manager is lifting restrictions on object sharing. You don't need to freeze objects to share them between threads, specifically:
- Top-level properties can be accessed and modified by any thread without using
@SharedImmutable
. - Objects passing through interop can be accessed and modified by any thread without freezing them.
Worker.executeAfter
no longer requires operations to be frozen.Worker.execute
no longer requires producers to return an isolated object subgraph.- Reference cycles containing
AtomicReference
andFreezableAtomicReference
do not cause memory leaks.
Apart from easy object sharing, the new memory manager also brings other major changes:
- Global properties are initialized lazily when the file they are defined in is accessed first. Previously global
properties were initialized at the program startup. As a workaround, you can mark
properties that must be initialized at the program start with the
@EagerInitialization
annotation. Before using, check its documentation. by lazy {}
properties support thread-safety modes and do not handle unbounded recursion.- Exceptions that escape
operation
inWorker.executeAfter
are processed like in other runtime parts, by trying to execute a user-defined unhandled exception hook or terminating the program if the hook was not found or failed with an exception itself. - Freezing is deprecated, disabled by default, and will be removed in future releases. Do not use freezing if you don't need your code to work with the legacy memory manager.
Follow these guidelines to migrate your projects from the legacy memory manager:
Update Kotlin
The new Kotlin/Native memory manager has been enabled by default since Kotlin 1.7.20. Check the Kotlin version and update to the latest one if necessary.
Update dependencies
Update to version 1.6.0 or later. Do not use versions with the native-mt
suffix.
There are also some specifics with the new memory manager you should keep in mind:
- Every common primitive (channels, flows, coroutines) works through the Worker boundaries, since freezing is not required.
Dispatchers.Default
is backed by a pool of Workers on Linux and Windows and by a global queue on Apple targets.- Use
newSingleThreadContext
to create a coroutine dispatcher that is backed by a Worker. - Use
newFixedThreadPoolContext
to create a coroutine dispatcher backed by a pool ofN
Workers. Dispatchers.Main
is backed by the main queue on Darwin and by a standalone Worker on other platforms.
The majority of libraries should work without any changes, however, there might be exceptions.
Make sure that you update dependencies to the latest versions, and there is no difference between library versions for the legacy and the new memory manager.
Update your code
To support the new memory manager, remove usages of the affected API:
Old API | What to do | |
---|---|---|
@SharedImmutable |
You can remove all usages, though there are no warnings for using this API in the new memory manager. | |
The FreezableAtomicReference class |
Use AtomicReference instead. |
|
The FreezingException class |
Remove all usages. | |
The InvalidMutabilityException class |
Remove all usages. | |
The IncorrectDereferenceException class |
Remove all usages. | |
The freeze() function |
Remove all usages. | |
The isFrozen property |
You can remove all usages. Since freezing is deprecated, the property always returns false . |
|
The ensureNeverFrozen() function |
Remove all usages. | |
The atomicLazy() function |
Use lazy() instead. |
|
The MutableData class |
Use any regular collection instead. | |
The WorkerBoundReference<out T : Any> class |
Use T directly. |
|
The DetachedObjectGraph<T> class |
Use T directly. To pass the value through the C interop, use the StableRef class. |
Support both new and legacy memory managers
If you're a library author and need to maintain support for the legacy memory manager or want to have a fallback in case of issues with the new memory manager, you can temporarily support code for both new and legacy memory managers.
To ignore deprecation warnings, do one of the following:
- Annotate usages of the deprecated API with
@OptIn(FreezingIsDeprecated::class)
. - Apply
languageSettings.optIn("kotlin.native.FreezingIsDeprecated")
to all the Kotlin source sets in Gradle. - Pass the compiler flag
-opt-in=kotlin.native.FreezingIsDeprecated
.
See Opt-in requirements for more details.