Компакция памяти в ядре Linux - это механизм, который перемещает страницы памяти для создания непрерывных блоков свободной памяти. Когда система работает длительное время, память становится фрагментированной: свободные страницы разбросаны по всей адресной зоне, и хотя их общее количество может быть достаточным, они не образуют непрерывного блока нужного размера. Компакция перемещает занятые страницы, чтобы освободить непрерывные области памяти для важных выделений больших блоков, таких как огромные страницы или страницы для устройств DMA.

Разберём архитектуру компакции, механизмы её запуска, влияние на производительность и практические приёмы настройки.

Что такое компакция памяти и зачем она нужна

Бадди-аллокатор ядра Linux выделяет страницы блоками размером 2^n страниц (порядки от 0 до MAX_ORDER). Для обычных страниц размером 4 КБ это не проблема, так как даже отдельные страницы подходят. Но для огромных страниц (huge pages) размером 2 МБ или 1 ГБ требуются непрерывные блоки из нескольких страниц. Когда память фрагментирована, большие блоки недоступны, даже если общее количество свободной памяти велико.

Компакция решает эту проблему: она перемещает выделенные страницы по адресному пространству, чтобы освободить непрерывные области памяти. Это позволяет ядру выделять огромные страницы и страницы для устройств DMA, которым требуются непрерывные физические адреса, даже когда общая свободная память фрагментирована.

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

Как работает компакция памяти

Механизм компакции работает по следующему принципу:

Сначала ядро выбирает целевую зону памяти и порядок (размер блока) для компакции, затем сканирует зону в поисках подходящих страниц для перемещения. Найденные страницы перемещаются в другие свободные места, освобождая целевые блоки, после чего обновляются таблицы страниц и счётчики, чтобы отразить новые местоположения страниц.

Компакция работает с определенными типами страниц. Не все страницы могут быть перемещены: некоторые страницы зафиксированы в памяти (например, страницы ядра, страницы с закрепленными страницами, страницы устройств DMA), и их нельзя переместить. Ядро выбирает для перемещения только те страницы, которые можно безопасно переместить.

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

Типы страниц и миграция

Ядро Linux классифицирует страницы по типам миграции, которые определяют, как они могут быть перемещены при компакции:

Неподвижные страницы (unmovable) не могут быть перемещены: страницы ядра, страницы с закреплённой памятью, страницы устройств DMA. Восстанавливаемые страницы (reclaimable) могут быть освобождены и пересозданы: кэш страниц, буферы ядра. Перемещаемые страницы (movable) свободно перемещаются: страницы пользовательского пространства, анонимные страницы.

При компакции ядро сначала пытается переместить подвижные страницы, затем восстанавливаемые страницы, и только в крайнем случае неподвижные страницы. Это минимизирует влияние на производительность и снижает риск потери данных.

Когда происходит компакция

Компакция может происходить в нескольких ситуациях:

При выделении больших блоков, когда запрос не удаётся из-за фрагментации, ядро запускает компакцию, чтобы освободить нужный блок. Проактивная компакция запускается ядром для поддержания запаса свободных крупных блоков и управляется параметром vm.compaction_proactiveness. При нехватке памяти ядро запускает компакцию, чтобы освободить больше свободных блоков для выделения. Администратор может вручную запустить компакцию через запись в файл /proc/sys/vm/compact_memory.

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

Настройка компакции через sysfs

Ядро предоставляет несколько параметров для настройки компакции через sysfs:

Параметр vm.compaction_proactiveness управляет агрессивностью проактивной компакции: значение от 0 до 100, где 0 означает отключение проактивной компакции, а 100 - максимальную агрессивность, со значением по умолчанию 20. Запись единицы в файл /proc/sys/vm/compact_memory запускает ручную компакцию всех зон памяти.

Пример настройки проактивной компакции:
CODE0

Эта команда устанавливает средний уровень агрессивности проактивной компакции.

Мониторинг компакции

Для мониторинга компакции можно использовать следующие инструменты:

Файл /proc/pagetypeinfo показывает распределение страниц по типам и зонам, включая количество свободных, выделенных и перемещаемых страниц. Файл /proc/vmstat содержит счётчики различных событий ядра, включая счётчики компакции. Журнал ядра dmesg содержит сообщения о событиях компакции, включая начало и окончание компакции, количество перемещённых страниц и время выполнения.

Команда cat /proc/vmstat | grep compact показывает счетчики компакции, включая количество начатых и завершённых операций компакции, количество перемещённых страниц и время, затраченное на компакцию.

Порядок сканирования при компакции имеет значение для эффективности: migrating scanner и free scanner движутся навстречу друг другу, и компакция прекращается, когда они встречаются. Если перемещаемых страниц мало или свободных мест недостаточно, компакция завершается досрочно с частичным результатом. Для очень крупных запросов (1 ГБ huge pages) компакция может потребовать перемещения гигабайт и занимать секунды, поэтому такие выделения лучше обслуживать заранее созданным резервом, а не компакцией в рантайме.

Отдельный аспект - компакция в виртуализованных средах: гостевые системы компактируют свою память независимо от гипервизора, и выделение огромных страниц внутри гостя может потребовать компакции и в госте, и на хосте, умножая накладные расходы. Для виртуализованных нагрузок рекомендуется передавать огромные страницы в гостя через резерв hugetlbfs на хосте либо настраивать проактивную компакцию в гостях с учётом добавленных расходов на обоих уровнях.

Влияние компакции памяти на производительность

Компакция памяти может существенно повлиять на производительность системы, especialmente при частых или масштабных операциях компакции. Основные источники нагрузки:

Копирование страниц занимает время процессора и полосу пропускания памяти: для огромных страниц размером 2 МБ перемещение одной страницы требует передачи 2 МБ данных, что может временно заблокировать другие операции с памятью.

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

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

Для минимизации влияния на производительность рекомендуется:

Для минимизации влияния на производительность параметры компакции настраиваются в зависимости от нагрузки: на серверах с высокой нагрузкой частота компакции снижается, чтобы избежать избыточных накладных расходов. Огромные страницы стоит использовать только там, где они действительно нужны, поскольку они требуют непрерывных блоков памяти и увеличивают вероятность фрагментации. Счётчики компакции мониторятся, и параметры корректируются по наблюдаемому поведению: слишком частая или длительная компакция указывает на необходимость уменьшения фрагментации памяти.

Диагностика причин фрагментации начинается с анализа выделений: если workload выделяет много неподвижной памяти мелкими порциями, unmovable-блоки дробят зону. Инструменты трассировки выделений (page owner, /sys/kernel/debug/page_owner) показывают стек вызовов для каждой страницы, что позволяет найти источник фрагментации. Для долгоживущих систем анализ page owner после роста фрагментации выявляет утечки неподвижных выделений, которые иначе сложно отличить от естественной деградации.

Практический порядок настройки компакции

Настройка компакции памяти в сетевой инфраструктуре укладывается в шесть шагов:

  1. проанализировать требования к производительности и размеру страниц для каждой рабочей нагрузки;
  2. настроить параметры компакции через sysfs, включая vm.compaction_proactiveness и размеры окон приёмника;
  3. провести тестирование с реальными нагрузками и скорректировать параметры;
  4. настроить мониторинг счетчиков компакции через /proc/vmstat и dmesg;
  5. провести тестирование с реальными нагрузками и скорректировать параметры;
  6. зафиксировать настройки в процедурах провижининга, чтобы они применялись при каждой загрузке системы.

Итог у компакции памяти позитивный: она позволяет эффективно использовать память даже при высокой фрагментации, обеспечивает работу с огромными страницами и устройствами DMA, и помогает поддерживать стабильную работу системы при ограниченных ресурсах памяти.

Ограничения и соображения при настройке

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

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

Четвёртым важным аспектом является мониторинг. Без правильного мониторинга невозможно понять, работает ли компакция эффективно, и где именно возникают проблемы. Регулярный анализ метрик компакции позволяет выявлять проблемы на ранней стадии и принимать меры до того, как они повлияют на пользователей.

Типичные ошибки при настройке

Одна из частых ошибок -установка слишком частых компакций, что может привести к снижению производительности из-за накладных расходов на копирование страниц и обновление таблиц страниц. Другая ошибка - игнорирование особенностей разных типов страниц: certaines страницы не могут быть перемещены, и попытка их перемещения может привести к ошибкам или снижению производительности.

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

Пятым важным аспектом является мониторинг. Без правильного мониторинга невозможно понять, работает ли компакция эффективно, и где именно возникают проблемы. Регулярный анализ метрик компакции позволяет выявлять проблемы на ранней стадии и принимать меры до того, как они повлияют на пользователей.