Управление памятью в Kotlin/Native
В Kotlin/Native используется современный менеджер памяти, похожий на менеджеры памяти в JVM, Go и других широко используемых технологиях. Он обладает следующими возможностями:
Объекты хранятся в общей куче и доступны из любого потока.
Периодически выполняется трассирующая сборка мусора, которая удаляет объекты, недоступные из «корней», например локальных и глобальных переменных.
Сборщик мусора
Алгоритм сборщика мусора (GC) в Kotlin/Native постоянно развивается. В настоящее время он работает как конкурентный сборщик с маркировкой и очисткой (CMS), который не разделяет кучу на поколения.
GC выполняется в отдельном потоке и запускается на основе эвристик давления на память или по таймеру. Кроме того, его можно запустить вручную.
GC обрабатывает очередь маркировки параллельно в нескольких потоках, включая потоки приложения, поток GC и дополнительные потоки-маркеры. В процессе маркировки участвуют потоки приложения и как минимум один поток GC. По умолчанию этап маркировки выполняется одновременно с потоками приложения, что сокращает время приостановки GC. Отслеживать производительность GC можно с помощью журналов GC.
После завершения этапа маркировки 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, в приложении:
-
Чтобы включить эту функцию, задайте следующий параметр компилятора в файле
gradle.properties:kotlin.native.binary.enableSafepointSignposts=true
Откройте Xcode, выберите Продукт | Профилировать или нажмите Cmd + I. Это действие скомпилирует приложение и запустит Instruments.
В списке шаблонов выберите os_signpost.
Настройте его, указав
org.kotlinlang.native.runtimeв поле подсистема иsafepointв поле категория.-
Нажмите красную кнопку записи, чтобы запустить приложение и начать запись событий сигнальных меток:

Здесь каждый синий маркер на нижнем графике обозначает отдельное событие сигнальной метки — паузу GC.
Отключение сборщика мусора
Рекомендуется не отключать GC. Однако в некоторых случаях, например для тестирования, при возникновении проблем или в короткоживущей программе, его можно отключить. Для этого задайте следующую опцию бинарного файла в файле gradle.properties:
kotlin.native.binary.gc=noop
Потребление памяти
В 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
Включение поддержки строк в кодировке 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 по умолчанию.
Если ни один из этих вариантов не помог, создайте задачу в YouTrack.
Фоновые модульные тесты
В модульных тестах очередь главного потока не обрабатывается, поэтому не используйте Dispatchers.Main, если только он не замокан. Для создания мока вызовите Dispatchers.setMain из kotlinx-coroutines-test.
Если вы не используете kotlinx.coroutines или Dispatchers.setMain по какой-либо причине вам не подходит, попробуйте следующий обходной способ реализации средства запуска тестов:
Затем скомпилируйте бинарный файл тестов с параметром компилятора -e testlauncher.mainBackground.
Что дальше
© 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