An urban myth has circulated for years that Linux did a better job avoiding swapouts than FreeBSD, but this in fact is not true. What was actually occurring was that FreeBSD was proactively paging out unused pages in order to make room for more disk cache while Linux was keeping unused pages in core and leaving less memory available for cache and process pages.
Ну лучше чем Linux, да и пусть. Я не против. Но хуже самого непонимая процесса выделения памяти меня убивала Inactive память. Что это такое и можно ли «это» безболезненно использовать? Считать ли эту память доступной для использования приложением?
Под cut'ом больше вопросов чем ответов.
FAQ FreeBSD сообщает, что
16.2.
Why does top show very little free memory even when I have very few programs running?
The simple answer is that free memory is wasted memory. Any memory that programs do not actively allocate is used within the FreeBSD kernel as disk cache. The values shown by top(1) labeled as Inact, Cache, and Buf are all cached data at different aging levels. This cached data means the system does not have to access a slow disk again for data it has accessed recently, thus increasing overall performance. In general, a low value shown for Free memory in top(1) is good, provided it is not very low.
Хорошо, пусть это кеш какого-то уровня, но почему не поместить эту Inact память в Cache? Может быть потому, что она доступна для использования (как утверждают многочисленные форумчане) и пусть не моментально, но может быть выделена по запросу?
Попытаемся выяснить это практическим путём. Имеем:
# top -b 0
last pid: 1019; load averages: 0.21, 0.45, 0.24 up 0+00:03:33 14:26:30
28 processes: 1 running, 27 sleeping
Mem: 18M Active, 17M Inact, 130M Wired, 24M Buf, 3756M Free
Swap: 3852M Total, 3852M Free
То есть почти вся память Free и своп полностью свободен.
Теперь, чтобы задействовать доступную память, создадим tmpfs раздел.
# mkdir /tmp/gb
# mount -t tmpfs -o mode=01777,size=3221225472 tmpfs /tmp/gb
# df -h | egrep "(Filesystem|tmpfs)"
Filesystem Size Used Avail Capacity Mounted on
tmpfs 3.0G 4.0K 3.0G 0% /tmp/gb
При этом
# top -b 0
last pid: 1028; load averages: 0.09, 0.19, 0.17 up 0+00:09:30 14:32:27
28 processes: 1 running, 27 sleeping
Mem: 18M Active, 17M Inact, 130M Wired, 24M Buf, 3756M Free
Swap: 3852M Total, 3852M Free
Согласен, раз раздел просто создан/примонтирован, но свободен, то незачем выделять ему память.
Поместим в него файл.
# dd if=/dev/urandom of=/tmp/gb/file.txt bs=1M count=3k
3072+0 records in
3071+0 records out
3220176896 bytes transferred in 53.334672 secs (60376801 bytes/sec)
# df -h | egrep "(Filesystem|tmpfs)"
Filesystem Size Used Avail Capacity Mounted on
tmpfs 3.0G 3.0G 1.0M 100% /tmp/gb
3 гигабайта памяти занято, но при этом
# top -b 0
last pid: 1040; load averages: 0.19, 0.26, 0.20 up 0+00:16:40 14:39:37
28 processes: 1 running, 27 sleeping
Mem: 18M Active, 3088M Inact, 137M Wired, 24M Buf, 677M Free
Swap: 3852M Total, 3852M Free
они почему-то считаются Inact. Но раз она не Active, то попытаемся её задествовать. Набросаем небольшой «Hello, world!» для выделения памяти и её последующего освобождения:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main (int argc, char *argv[])
{
int i;
char *buffer[64];
long lSize = 1024*1024*1024;
int iMB = argc < 2 ? 1 : atoi(argv[1]);
printf("iMB:\t%d\n", iMB);
for(i=0; i < iMB; i++)
{
buffer[i] = (char*) malloc (lSize);
if(buffer[i] != NULL)
{
printf("Alloc: %d\n", i);
memset(buffer[i], 127, lSize);
} else printf("Error!\n");
sleep(1);
}
sleep(10);
for(i=0; i < iMB; i++)
{
printf("Free: %d\n", i);
free (buffer[i]);
}
return 0;
}
# time ./a.out 3
iMB: 3
Alloc: 0
Alloc: 1
Alloc: 2
Free: 0
Free: 1
Free: 2
0.915u 1.475s 1:00.16 3.9% 5+168k 0+0io 0pf+0w
Почему так долго? Вероятно наш tmpfs, якобы находящийся в Inactive, выдавливался в своп.
Дествительно, при выделении памяти (момент «sleep(10);» в коде) видим:
# top -b 0
last pid: 1128; load averages: 0.02, 0.11, 0.14 up 0+00:28:34 14:51:31
37 processes: 1 running, 36 sleeping
Mem: 3106M Active, 621M Inact, 155M Wired, 26M Cache, 27M Buf, 14M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse
Но хуже другое. После освобождения памяти приложением:
# top -b 0
last pid: 1129; load averages: 0.09, 0.12, 0.15 up 0+00:28:48 14:51:45
36 processes: 1 running, 35 sleeping
Mem: 33M Active, 621M Inact, 145M Wired, 26M Cache, 27M Buf, 3095M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse
своп остался задействован.
Обращения к файлу вновь вернули память в Inact
# time dd of=/dev/zero if=/tmp/gb/file.txt bs=1M count=3k
3071+0 records in
3071+0 records out
3220176896 bytes transferred in 40.265654 secs (79973292 bytes/sec)
0.008u 3.796s 0:40.26 9.4% 22+154k 0+0io 0pf+0w
# time dd of=/dev/zero if=/tmp/gb/file.txt bs=1M count=3k
3071+0 records in
3071+0 records out
3220176896 bytes transferred in 1.242623 secs (2591434941 bytes/sec)
0.000u 1.241s 0:01.24 100.0% 25+173k 0+0io 0pf+0w
# top -b 0
last pid: 1144; load averages: 0.09, 0.12, 0.14 up 0+00:36:22 14:59:19
36 processes: 1 running, 35 sleeping
Mem: 29M Active, 3077M Inact, 146M Wired, 4K Cache, 27M Buf, 669M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse
При этом непонятно почему остался Swap: 2502M Used
Но, допустим, это нормально и при наличии свободного свопа можно считать эту задействанную память неактивной и помечать её как Inact. Что же будет, если у нас нет свопа? Надеюсь, теперь задействованная память теперь будет Active.
Убираем своп, аналогично монтируем 3Gb раздел tmpfs и заполняем его.
# top -b 0
last pid: 1013; load averages: 0.58, 0.53, 0.29 up 0+00:05:03 15:11:46
34 processes: 1 running, 33 sleeping
Mem: 21M Active, 3089M Inact, 138M Wired, 24M Buf, 673M Free
Swap:
Для очистки совести убедимся что top не врёт:
# expr `sysctl -n vm.stats.vm.v_inactive_count` \* `sysctl -n vm.stats.vm.v_page_size`
3239620608
И, несмотря на отсутствие свопа, наша занятая память по прежнему не совсем активна… Раз она не активна попробуем её вновь занять нашим приложением.
# ./a.out 3
iMB: 3
Alloc: 0
Killed
Ч.т.д. и, наконец:
# top -b 0
last pid: 1026; load averages: 0.15, 0.22, 0.21 up 0+00:11:37 15:18:20
34 processes: 1 running, 33 sleeping
Mem: 3102M Active, 1524K Inact, 138M Wired, 200K Cache, 24M Buf, 679M Free
Swap:
# expr `sysctl -n vm.stats.vm.v_inactive_count` \* `sysctl -n vm.stats.vm.v_page_size`
1720320
Здесь, наверное, должен быть какой-то вывод, но его нет…
Комментарии (19)
lexore
26.01.2016 19:15Мне кажется, это проблема tmpfs.
Я бы рекомендовал попробовать повторить эксперимент, но вместо tmpfs написать программу, которая захавает память и останется работать.
При этом непонятно почему остался Swap: 2502M Used
По личному опыту (и смутной памяти документации), счетчик used у свопа работает по принципу «только прибавляем».
Т.е. swap used показывает, на сколько своп был заюзан вообще с момента примонтирования свопа.
Отчасти на это намекает «used», а не «in use».
Причем, как в linux, так и во freebsd.simpleadmin
26.01.2016 21:55счетчик used у свопа работает по принципу «только прибавляем».
[19:32]# swapinfo
Device 1K-blocks Used Avail Capacity
/dev/ada0b 5022720 311000 4711720 6%
[21:30]# swapinfo
Device 1K-blocks Used Avail Capacity
/dev/ada0b 5022720 309288 4713432 6%
т.е. работает и на освобожение.
Другой вопрос зачем он вообще используется при 13G Free (и с момента старта системы Free ниже 12 не опускалась).
Mem: 20M Active, 244M Inact, 2206M Wired, 10M Cache, 1643M Buf, 13G Free
Swap: 4905M Total, 303M Used, 4602M Free, 6% Inuse
Но вопрос свопа второстепенен.
Мне кажется, это проблема tmpfs.
Нет, проблемы проявляются и без него. Я задействовал tmpfs просто для наглядности.
amarao
27.01.2016 00:01+2Во-первых, в линуксе есть vm.swappiness, указывающий степень свопливости линукса — выше величина, сильнее упреждающий своппинг. 0 отключает.
Во-вторых, подсчёт свободной памяти на linux'е — катастрофа. Предсказать, когда именно придёт oom почти невозможно, ибо free уже нет давно, а какую часть cached/buffers можно выкинуть даже само ядро толком не знает (tmpfs жрёт buffers, но выкинуть из памяти нельзя).
Видимо, у freebsd с этим не легче. Очень хочется, чтобы кто-то научился считать реальную свободную память.
А ещё очень хочется научить ограничивать paging. Потому что одному «свободная память потерянная память», а другому — просранная оперативка виртуалок, которые дважды кешируют то, что хост уже и так закешировал и отдаст им бысто.pansa
27.01.2016 00:59Ну если есть какая-нибудь возможность определить какие именно файлы и когда не нужно кэшировать, то может поможет fadvise?
grafmishurov
27.01.2016 03:07QEMU also supports a wide variety of caching modes. If you're using raw volumes or partitions, it is best to avoid the cache completely, which reduces data copies and bus traffic
amarao
27.01.2016 13:58Я бы предпочёл иметь ситуацию, когда объём кеширования управляется хостом, а не гостями. Сейчас картинка такая: у гостя 64Гб, реальных данных в памяти на гигабайт, 63 гигабайта протухшего кеша которым никто не будет пользоваться. У другого гостя два гигабайта памяти и из них полтора занято данными. На выходе явная диспропорция по объёму IO с дисков (второй гость делает тяжёлое IO снова и снова). Если бы кеш был на хосте, то нагрузка была бы распределена более равномерно.
zuborg
27.01.2016 16:08Чтобы кеш был на хосте, нужно чтобы FS была на хосте. Если хост не видет, с какого места на диске поднята страница, он не имеет права её освобождать.
Вообще FreeBSD ведет подсчет интенсивности страниц (Linux, скорее всего, тоже). В Вашем случае можно второму гостю выделить тоже 64Г, и на хосте поставить своп (лучше на SSD) на пару сот гиг. ОС определит какие страницы не используются и положит их в своп.amarao
27.01.2016 19:24Ну, общепринятым является хранить образа на файловой системе хоста. Кроме специфичных случаев с SAN/общими блочными устройствами, это де-факто стандарт.
Проблема в другом: гость не видит нагрузки на хост и не может адекватно реагировать на «больше кеша, меньше кеша».zuborg
27.01.2016 20:22Каюсь, поспешил. Хотя хост не может непосредственно знать, какая страница в виртуалке была поднята с какого места на диске (разве что ядро гостя об этом сообщит хосту), но он может самостоятельно кешировать страницы диска. Т.е. в Вашем случае нужно ограничить память виртуалок до необходимого приложениям объема, не включая дисковый кеш, и позволить хосту заниматься кешированием диска. Не самый идеальный вариант, строго говоря (двойное кеширование, гости под прессингом нехватки памяти могут заниматься лишним свопингом..), но зато память будет более эффективно использоваться.
amarao
27.01.2016 20:34В современных линуксах невозможно определить «необходимый объём». Потому что придёт OOM killer и всем станет понятно, что необходимый объём был другим. Полагаться на vss нельзя (он бывает кратно больше нужного объёма), отключать оверкоммит нельзя.
Увы.
Ivan_83
27.01.2016 00:59Я люблю фрю, но вот с памятью там, ИМХО, хреновато, при отключённом свопе.
Например.
1. Есть rtorrent и он качает нечто что ощутимо больше размера оперативы. Если качает слишком быстро то начинается OMM.
Однажды меня это сильно достало и я накидал багрепорт: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=195882
но не простой, а с мелкой программкой которая демонстрирует наступление OMM при отключённом свопе, при этом сама прога вообще память не потребляет. Ядро держит память ровно до того момента пока ты не прочитаешь то что она записала в файл, либо файл удалишь.
Закрытие файла, всякие флюши не помогают совсем.
2. VirtualBox.
Очень любит почему то сжирать память при интенсивных дисковых в/в, даже если кеш отключен, и тоже приходит OOM.
Аркадий говорит что так и должно быть с отключённым свопом, я с ним не согласен: не должно и свпо мне нафик не сдался. Но я не разработчик фрёвой VM и чес говоря нихрена там не понимаю как оно устроено, так что даже приватного патча к ядру для себя любимого я не осилил. :(
По моим наблюдениям есть ещё некоторая память ядра, которая в top не отображается, а может нужно лучше смотреть в vmstat -z :)
grafmishurov
27.01.2016 03:36Пользовательская документация, мне так кажется, не поможет в таком болезненном для ядра вопросе. Алгоритмов несколько, в Линуксе зависит еще от аллокатора, выбранного при настройке исходников ядра, несколько уровней зонирования, buddy system, slab, помимо pages на page frames, необходимо, чтобы память сильно не фрагментировалась, многое хранится в ней до самого запроса на освобождение и т.д. Лучше сразу исходники читать и комментарии непосредственно к коду ядра. Таким методом интроспекции (top и malloc) вряд ли можно даже на интуитивном уровне догадаться, что внутри происходит.
robert_ayrapetyan
27.01.2016 04:22+1Как будто в каких-то других ОС можно адекватно понять что к чему с памятью (в той же Windows всегда занято 95% физической памяти), поэтому это не «амнезия FreeBSD», а более фундаментальная проблема. Тяжело учесть и грамотно показать все распределение памяти в современной ОС. А если сюда добавить еще shared memory (некоторые подвиды которой вы вообще нигде не увидите никакими средствами в FreeBSD (показывает только System V)), то вообще печально.
В фре пользуюсь htop, который работает через прослойку linux procfs, которая дополнительно вносит свою лепту в вычисления, но понять самое важное всегда можно. В списке процессов в колонке RES всегда значение, которому можно верить. По наблюдениям, когда сумма чисел всех процессов в RES приближается к физическому размеру памяти, начинает расти swap. Который потом уменьшается по странным законам (а иногда и не уменьшается вовсе, как вы и описали).grafmishurov
27.01.2016 11:50+1Менеджмент памяти ядром — это тема, скажем, примерно на 700 страниц, тут в двух словах не пересказать, не то, что «аршином измерить». Погуглите слова «linux kernel memory management pdf», если на kernel(dot)org отправит, то это то, меня, к сожалению, местные крепкие ребята лишили возможности постить ссылки (чем-то не понравилась, возможно резкостью выражений, моя приязнь к С и неприязнь к С++ в одном из тредов Интела). Не факт, что суммирование процессами занятой памяти даст адекватную картину, но это, мне кажется, ближе к теме. Ну и сама по себе задача измерить доступную память очень зыбка, даже Гугл не решается ее измерять перед компиляцией Хромиума в Генте, оценивает свободное место на диске и общее количество оперативки.
Zelgadis
29.01.2016 01:53htop насверное самый не аккуратный способ посмотреть память на freebsd. Начиная от того, что ZFS ARC пропадает из всех вычислений, заканчивая что cpu load тоже верить нельзя.
У меня swap растет при «свободных» 15 гигабайтах памяти. После пары часов работы, из 32 остается «свободных» в районе 5, и только потому, что на диске занимаего простраства меньше чем оперативной памяти. Hit rate в ARC так совсем до 98%. Tmpfs руками нигде не используется. Не жалуюсь, но от нормального htop'a не отказался бы.
zuborg
Документация FreeBSD говорит нам, что немедленному освобождению допускается только cache страницы, а не inactive.
Active,Inactive,Cache,Free — грубо говоря, это 4 пула страниц, осортированные по частоте использования. OS перемещает страницы из пула в пул в зависимости от того, насколько активно они используются.
Пул Free поддерживается ненулевым, чтобы во время срочного выделения страницы (например, во время прерывания) не возникло отказа или паузы.
Cache страницы имеют дисковое отображение — это либо копия страницы из файла, либо имеет копию в свопе — в обоих случаях её можно немедленно освободить, а если понадобится — прочитаем с диска.
Active и Inactive соотв — часто (активно) и нечасто используемые страницы. Они могут как иметь дисковое отображение, так могут и не иметь (в этом случае при нехватке памяти они вытесняются в своп)
simpleadmin
К сожалению и это не отвечает на вопрос «сколько в данный момент может быть доступно памяти приложению?»
Выделяю 100M блоками пока не произойдёт исключение и освобождаю.
В итоге Cache вообще остался нетронут, Inact похудел на 52G. Но как выяснить, что эти 52G были доступны?