Kotlin Help

Migrate to the new memory manager

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 and always disabled.

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

kotlinx.coroutines

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.

Ktor

Update to version 2.0 or later.

Other dependencies

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.

What's next

Last modified: 25 September 2024