迁移到新版内存管理器

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 and FreezableAtomicReference 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 in Worker.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 of N Workers.
  • Dispatchers.Main is backed by the main queue on Darwin and by a standalone Worker on other platforms.
  • Update to version 2.0 or later.

    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.

    What's next