Есть ли способ обнаружить фрагментацию памяти в Linux? Это связано с тем, что на некоторых долго работающих серверах я заметил снижение производительности, и только после перезапуска процесса я увидел лучшую производительность. Я заметил это больше при использовании поддержки огромных страниц в Linux - огромные страницы в Linux более подвержены фрагментации?
В частности, я просмотрел / proc / buddyinfo. Я хочу знать, есть ли какие-то лучшие способы (не только команды CLI как таковые, подойдут любые программы или теоретические основы), чтобы взглянуть на это.
Я отвечаю на linux тег. Мой ответ касается только Linux.
Да, огромные страницы более подвержены фрагментации. Существует два вида памяти: одно, которое получает ваш процесс (виртуальное), и второе, которым управляет ядро (реальное). Чем больше любая страница, тем сложнее будет сгруппировать (и сохранить) ее соседей, особенно когда ваша служба работает в системе, которая также должна поддерживать другие, которые по умолчанию выделяют и записывают в гораздо больше памяти, чем они фактически в конечном итоге использовать.
Отображение ядром (реальных) предоставленных адресов является частным. Есть очень веская причина, по которой пользовательское пространство видит их так, как их представляет ядро, потому что ядро должно иметь возможность переопределить, не запутывая пользовательское пространство. Ваш процесс становится красивым, непрерывным "Диснейфид" адресное пространство, в котором работать, не обращая внимания на то, что на самом деле представляет собой ядро делать с той памятью за кулисами.
Причина, по которой вы видите снижение производительности на долго работающих серверах, скорее всего, связана с выделенными блоками, которые не были явно заблокированы (например, mlock()
/mlockall()
или posix_madvise()
) и долгое время не изменялись, были выгружен, что означает, что ваш сервис перемещается на диск, когда ему нужно их прочитать. Изменение этого поведения делает ваш процесс плохой сосед, поэтому многие люди размещают свои СУБД на совершенно другом сервере, чем web / php / python / ruby / что-то еще. Единственный разумный способ исправить это - уменьшить конкуренцию за смежные блоки.
Фрагментация действительно заметна (в большинстве случаев), когда страница A находится в памяти, а страница B перемещена для подкачки. Естественно, перезапуск вашей службы, казалось бы, «вылечит» это, но только потому, что ядро еще не имело возможности вывести процесс (сейчас) вновь выделенных блоков в пределах его коэффициента избыточной фиксации.
Фактически, перезапуск (скажем,) 'apache' при высокой нагрузке, скорее всего, отправит блоки, принадлежащие другим службам, прямо на диск. Так что да, apache может улучшиться на короткое время, но mysql может пострадать ... по крайней мере, до тех пор, пока ядро не заставит их страдать одинаково, когда просто недостаточно физической памяти.
Добавьте больше памяти или разделите требовательные malloc()
потребители :) Это не просто фрагментация, на которую нужно смотреть.
Пытаться vmstat
чтобы получить обзор того, что на самом деле и где хранится.
Чтобы получить текущий индекс фрагментации, используйте:
sudo cat /sys/kernel/debug/extfrag/extfrag_index
Чтобы дефрагментировать память ядра, попробуйте выполнить:
sysctl vm.compact_memory=1
Также вы можете отключить прозрачные огромные страницы (также известные как THP) и / или отключить подкачку (или уменьшить swappiness
).
Чтобы уменьшить фрагментацию пользовательского пространства, вы можете попробовать другой распределитель, например jemalloc
(у него отличный возможности самоанализа, что даст вам представление о внутренней фрагментации распределителя).
Вы можете переключиться на настраиваемый malloc, перекомпилировав свою программу с ним или просто запустив свою программу с LD_PRELOAD
: LD_PRELOAD=${JEMALLOC_PATH}/lib/libjemalloc.so.1 app
(остерегаться взаимодействие между THP и распределителями памяти)
Хотя это немного не связано с фрагментацией памяти (но связано с уплотнением / миграцией памяти), вы, вероятно, захотите запустить несколько экземпляров своей службы, по одному для каждого узла NUMA, и связать их с помощью numactl
.
Использование огромных страниц не должно вызывать дополнительной фрагментации памяти в Linux; Linux поддерживает огромные страницы только для общей памяти (через shmget или mmap), и любые используемые огромные страницы должны быть специально запрошены и заранее выделены системным администратором. Попав в память, они там закрепляются и не выгружаются. Проблема подкачки огромных страниц перед лицом фрагментации памяти заключается именно в том, почему они остаются закрепленными в памяти (при выделении огромной страницы размером 2 МБ ядро должно найти 512 смежных свободных страниц размером 4 КБ, которые могут даже не существовать).
Документация по Linux на огромных страницах: http://lwn.net/Articles/375098/
Есть одно обстоятельство, при котором фрагментация памяти может привести к медленному распределению огромных страниц (но не там, где огромные страницы причина фрагментация памяти), и это если ваша система настроена на увеличение пула огромных страниц по запросу приложения. Если / proc / sys / vm / nr_overcommit_hugepages больше, чем / proc / sys / vm / nr_hugepages, это может произойти.
Там есть /proc/buddyinfo
что очень полезно. Это более полезно с красивым форматом вывода, например, этот сценарий Python может:
https://gist.github.com/labeneator/9574294
Для огромных страниц вам нужны бесплатные фрагменты размером 2097152 (2 МБ) или больше. Для прозрачных огромных страниц он автоматически сжимается, когда ядро запрашивает некоторые, но если вы хотите увидеть, сколько вы можете получить, запустите от имени root:
echo 1 | sudo tee /proc/sys/vm/compact_memory
Также да, огромные страницы вызывают большие проблемы с фрагментацией. Либо вы не можете получить огромные страницы, либо их присутствие заставляет ядро тратить много дополнительного времени, пытаясь их получить.
У меня есть решение, которое мне подходит. Я использую его на нескольких серверах и на своем ноутбуке. Он отлично работает с виртуальными машинами.
Добавить kernelcore=4G
в командной строке ядра Linux. На моем сервере я использую 8G. Будьте осторожны с числом, потому что это помешает вашему ядру выделить что-либо вне этой памяти. Серверы, которым требуется много буферов сокетов или которые выполняют потоковую запись с диска на сотни дисков, не будут иметь таких ограничений. Любое выделение памяти, которое должно быть «закреплено» для slab или DMA, относится к этой категории.
Вся остальная память становится «подвижной», что означает, что ее можно сжать в красивые фрагменты для выделения огромных страниц. Теперь прозрачные огромные страницы действительно могут взлететь и работать так, как должны. Когда ядру требуется больше 2M страниц, оно может просто переназначить страницы 4K в другое место.
И я не совсем уверен, как это взаимодействует с прямым вводом-выводом с нулевым копированием. Память в «подвижной зоне» не должна быть закреплена, но прямой запрос ввода-вывода сделает именно это для DMA. Он может скопировать это. В любом случае он может закрепить его в подвижной зоне. В любом случае, вероятно, это не совсем то, что вам нужно.