kotlin / 1.7 / docs / native-memory-manager.html

Kotlin/Native memory management

Kotlin/Native uses a modern memory manager that is similar to JVM, Go, and other mainstream technologies:

  • Objects are stored in a shared heap and can be accessed from any thread.

  • Tracing garbage collector (GC) is executed periodically to collect objects that are not reachable from the "roots", like local and global variables.

The memory manager is the same across all the Kotlin/Native targets, except for wasm32, which is only supported in the legacy memory manager.

Garbage collector

The exact algorithm of GC is constantly evolving. As of 1.7.20, it is the Stop-the-World Mark and Concurrent Mark Sweep collector that does not separate heap into generations.

GC is executed on a separate thread and kicked off based on the timer and memory pressure heuristics, or can be called manually.

Enable garbage collection manually

To force start garbage collector, call kotlin.native.internal.GC.collect(). It triggers a new collection and waits for its completion.

Monitor GC performance

There are no special instruments to monitor the GC performance yet. However, it's still possible to look through GC logs for diagnosis. To enable logging, set the following compilation flag in the Gradle build script:

-Xruntime-logs=gc=info

Currently, the logs are only printed to stderr.

Disable garbage collection

It's recommended to keep GC enabled. However, you can disable it in certain cases, for example, for testing purposes or if you encounter issues and have a short-lived program. To do that, set the following compilation flag in the Gradle build script:

-Xgc=noop

Memory consumption

If there are no memory leaks in the program, but you still see unexpectedly high memory consumption, switch the memory allocator from mimalloc, which is used by default on many targets to a system one. For that, set the following compilation flag in the Gradle build script:

-Xallocator=std
  • If the memory consumption goes down to the expected levels, everything is OK. The mimalloc allocator pre-allocates system memory for performance reasons.

  • If the memory consumption still doesn't go down, report an issue in YouTrack.

Unit tests in the background

In unit tests, nothing processes the main thread queue, so don't use Dispatchers.Main unless it was mocked, which can be done by calling Dispatchers.setMain from kotlinx-coroutines-test.

If you don't rely on kotlinx.coroutines or Dispatchers.setMain doesn't work for you for some reason, try the following workaround for implementing the test launcher:

package testlauncher import platform.CoreFoundation.* import kotlin.native.concurrent.* import kotlin.native.internal.test.* import kotlin.system.* fun mainBackground(args: Array<String>) { val worker = Worker.start(name = "main-background") worker.execute(TransferMode.SAFE, { args.freeze() }) { val result = testLauncherEntryPoint(it) exitProcess(result) } CFRunLoopRun() error("CFRunLoopRun should never return") }

Then, compile the test binary with the -e testlauncher.mainBackground compiler flag.

Legacy memory manager

If it's necessary, you can switch back to the legacy memory manager. Set the following option in your gradle.properties:

kotlin.native.binary.memoryModel=strict

If you encounter issues with migrating from the legacy memory manager, or you want to temporarily support both the current and legacy memory managers, see our recommendations in the migration guide.

What's next

Last modified: 22 September 2022

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/native-memory-manager.html