Spec-Zone.ru › Kotlin 2

Управление памятью в Kotlin/Native

В Kotlin/Native используется современный менеджер памяти, похожий на менеджеры памяти в JVM, Go и других широко используемых технологиях. Он обладает следующими возможностями:

  • Объекты хранятся в общей куче и доступны из любого потока.

  • Периодически выполняется трассирующая сборка мусора, которая удаляет объекты, недоступные из «корней», например локальных и глобальных переменных.

Сборщик мусора

Алгоритм сборщика мусора (GC) в Kotlin/Native постоянно развивается. В настоящее время он работает как конкурентный сборщик с маркировкой и очисткой (CMS), который не разделяет кучу на поколения.

GC выполняется в отдельном потоке и запускается на основе эвристик давления на память или по таймеру. Кроме того, его можно запустить вручную.

GC обрабатывает очередь маркировки параллельно в нескольких потоках, включая потоки приложения, поток GC и дополнительные потоки-маркеры. В процессе маркировки участвуют потоки приложения и как минимум один поток GC. По умолчанию этап маркировки выполняется одновременно с потоками приложения, что сокращает время приостановки GC. Отслеживать производительность GC можно с помощью журналов GC.

Параллельное выполнение этапа маркировки можно отключить с помощью параметра компилятора kotlin.native.binary.gcMarkSingleThreaded=true. Однако на больших кучах это может увеличить время приостановки сборщика мусора.

После завершения этапа маркировки GC обрабатывает слабые ссылки и обнуляет ссылки на немаркированные объекты. По умолчанию слабые ссылки обрабатываются параллельно, чтобы сократить время приостановки GC.

Если у вас возникли проблемы с CMS, вернитесь к конфигурации параллельной маркировки и конкурентной очистки (PMCS). Для этого задайте следующую опцию бинарного файла в файле gradle.properties:

kotlin.native.binary.gc=pmcs

Запуск сборщика мусора вручную

Чтобы принудительно запустить сборщик мусора, вызовите kotlin.native.internal.GC.collect(). Этот метод запускает новую сборку мусора и ожидает её завершения.

Мониторинг производительности GC

Чтобы отслеживать производительность GC и диагностировать проблемы, можно просматривать его журналы. Для включения ведения журнала задайте следующий параметр компилятора в скрипте сборки Gradle:

-Xruntime-logs=gc=info

В настоящее время журналы выводятся только в stderr.

На платформах Apple для отладки производительности приложений iOS можно использовать инструментарий Xcode Instruments. Сборщик мусора сообщает о паузах с помощью сигнальных меток, доступных в Instruments. Сигнальные метки позволяют вести пользовательское журналирование в приложении и проверять, соответствует ли пауза GC зависанию приложения.

Чтобы отслеживать паузы, связанные с GC, в приложении:

  1. Чтобы включить эту функцию, задайте следующий параметр компилятора в файле gradle.properties:

    kotlin.native.binary.enableSafepointSignposts=true
    
  2. Откройте Xcode, выберите Продукт | Профилировать или нажмите Cmd + I. Это действие скомпилирует приложение и запустит Instruments.

  3. В списке шаблонов выберите os_signpost.

  4. Настройте его, указав org.kotlinlang.native.runtime в поле подсистема и safepoint в поле категория.

  5. Нажмите красную кнопку записи, чтобы запустить приложение и начать запись событий сигнальных меток:

    Tracking GC pauses as signposts

    Здесь каждый синий маркер на нижнем графике обозначает отдельное событие сигнальной метки — паузу GC.

Отключение сборщика мусора

Рекомендуется не отключать GC. Однако в некоторых случаях, например для тестирования, при возникновении проблем или в короткоживущей программе, его можно отключить. Для этого задайте следующую опцию бинарного файла в файле gradle.properties:

kotlin.native.binary.gc=noop

Если эта опция включена, GC не удаляет объекты Kotlin, поэтому потребление памяти будет постоянно расти, пока работает программа. Следите за тем, чтобы не исчерпать системную память.

Потребление памяти

В Kotlin/Native используется собственный аллокатор памяти. Он делит системную память на страницы, что позволяет выполнять очистку независимо в последовательном порядке. Каждое выделение памяти становится блоком на странице, а страница отслеживает размеры блоков. Разные типы страниц оптимизированы для различных размеров выделяемой памяти. Последовательное расположение блоков памяти обеспечивает эффективный перебор всех выделенных блоков.

При выделении памяти поток ищет подходящую страницу в зависимости от размера выделения. Потоки хранят набор страниц для разных категорий размеров. Обычно текущая страница для заданного размера подходит для выделения. Если это не так, поток запрашивает другую страницу из общего пула выделения памяти. Такая страница может быть уже доступна, требовать очистки или её может понадобиться сначала создать.

Аллокатор памяти Kotlin/Native защищает от резких всплесков выделения памяти. Он предотвращает ситуации, когда мутатор начинает быстро выделять большое количество мусора, а поток GC не успевает за ним, из-за чего потребление памяти бесконечно растёт. В этом случае GC принудительно запускает этап остановки мира до завершения итерации.

Вы можете самостоятельно отслеживать потребление памяти, проверять наличие утечек и настраивать использование памяти.

Мониторинг потребления памяти

Для отладки проблем с памятью можно проверять метрики менеджера памяти. Кроме того, на платформах Apple можно отслеживать потребление памяти Kotlin.

Проверка утечек памяти

Чтобы получить доступ к метрикам менеджера памяти, вызовите kotlin.native.internal.GC.lastGCInfo(). Этот метод возвращает статистику последнего запуска сборщика мусора. Эта статистика может быть полезна для следующих задач:

  • Отладка утечек памяти при использовании глобальных переменных

  • Проверка утечек при запуске тестов

import kotlin.native.internal.*
import kotlin.test.*

class Resource

val global = mutableListOf<Resource>()

@OptIn(ExperimentalStdlibApi::class)
fun getUsage(): Long {
    GC.collect()
    return GC.lastGCInfo!!.memoryUsageAfter["heap"]!!.totalObjectsSizeBytes
}

fun run() {
    global.add(Resource())
    // The test will fail if you remove the next line
    global.clear()
}

@Test
fun test() {
    val before = getUsage()
    // A separate function is used to ensure that all temporary objects are cleared
    run()
    val after = getUsage()
    assertEquals(before, after)
}

Отслеживание потребления памяти на платформах Apple

При отладке проблем с памятью на платформах Apple можно посмотреть, сколько памяти зарезервировано кодом Kotlin. Память, используемая Kotlin, помечена идентификатором, и её можно отслеживать с помощью таких инструментов, как VM Tracker в Xcode Instruments.

Эта функция доступна только для аллокатора памяти Kotlin/Native по умолчанию, если соблюдены все следующие условия:

  • Маркировка включена. Память должна быть помечена допустимым идентификатором. Apple рекомендует использовать числа от 240 до 255; значение по умолчанию — 246.

    Если задано свойство Gradle kotlin.native.binary.mmapTag=0, маркировка отключается.

  • Выделение памяти с помощью mmap. Аллокатор должен использовать системный вызов mmap для отображения файлов в память.

    Если задано свойство Gradle kotlin.native.binary.disableMmap=true, аллокатор по умолчанию использует malloc вместо mmap.

  • Подкачка включена. Подкачка выделенной памяти (буферизация) должна быть включена.

    Если задано свойство Gradle kotlin.native.binary.pagedAllocator=false, память резервируется отдельно для каждого объекта.

Настройка потребления памяти

Если потребление памяти неожиданно велико, попробуйте следующие решения:

Обновление Kotlin

Обновите Kotlin до последней версии. Мы постоянно совершенствуем менеджер памяти, поэтому даже простое обновление компилятора может снизить потребление памяти.

Отключение подкачки аллокатора

Можно отключить подкачку выделенной памяти (буферизацию), чтобы аллокатор памяти резервировал память отдельно для каждого объекта. В некоторых случаях это помогает соблюдать строгие ограничения по памяти или снизить потребление памяти при запуске приложения.

Для этого задайте следующую опцию в файле gradle.properties:

kotlin.native.binary.pagedAllocator=false

Если подкачка аллокатора отключена, отслеживать потребление памяти на платформах Apple невозможно.

Включение поддержки строк в кодировке Latin-1

По умолчанию строки в Kotlin хранятся в кодировке UTF-16, где каждый символ занимает два байта. В некоторых случаях из-за этого строки занимают в бинарном файле вдвое больше места, чем в исходном коде, а чтение данных требует вдвое больше памяти.

Чтобы уменьшить размер бинарного файла приложения и скорректировать потребление памяти, можно включить поддержку строк в кодировке Latin-1. В кодировке Latin-1 (ISO 8859-1) каждый из первых 256 символов Unicode представлен одним байтом.

Чтобы включить эту возможность, задайте следующую опцию в файле gradle.properties:

kotlin.native.binary.latin1Strings=true

При включённой поддержке Latin-1 строки хранятся в этой кодировке, если все символы входят в её диапазон. В противном случае используется кодировка UTF-16 по умолчанию.

Пока функция находится в статусе Experimental, функции расширения cinterop String.pin, String.usePinned и String.refTo работают менее эффективно. Каждый их вызов может запускать автоматическое преобразование строки в UTF-16.

Если ни один из этих вариантов не помог, создайте задачу в YouTrack.

Фоновые модульные тесты

В модульных тестах очередь главного потока не обрабатывается, поэтому не используйте Dispatchers.Main, если только он не замокан. Для создания мока вызовите Dispatchers.setMain из kotlinx-coroutines-test.

Если вы не используете kotlinx.coroutines или Dispatchers.setMain по какой-либо причине вам не подходит, попробуйте следующий обходной способ реализации средства запуска тестов:

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") }

Затем скомпилируйте бинарный файл тестов с параметром компилятора -e testlauncher.mainBackground.

Что дальше

  • Переход с устаревшего менеджера памяти

  • Подробнее об интеграции со Swift/Objective-C ARC

29 мая 2026 г.
Библиотеки платформыИнтеграция со Swift/Objective-C ARC

© 2010–2026 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API