Как вернуть память из процесса в ОС

У меня проблема с управлением памятью в различных операционных системах.

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

В AIX и Solaris я наблюдаю следующее поведение:

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

Когда эту память можно будет вернуть обратно в ОС? Как я могу это сделать?

Линукс другой: оказывается, он иногда возвращает память, но я не могу понять, когда и как. У меня есть, например, сценарий, в котором процесс перед запросом был 100 МБ, затем 700 МБ в пике, а после освобождения всего этого он уменьшился до 600 МБ. Я не понимаю - если линукс отдает память ОС, то почему не всю?


person ModdyFire    schedule 21.08.2012    source источник
comment
Я вижу, что Java 12 теперь обещает вернуть память ОС при некоторых обстоятельствах — openjdk.java.net /jeps/346   -  person Brian Agnew    schedule 27.02.2019


Ответы (5)


Библиотека glibc (которая обычно используется в качестве стандартной библиотеки C в Linux) может выделять память двумя способами — с помощью sbrk() или с помощью mmap(). Он будет использовать mmap() для достаточно больших распределений.

Память, выделенная с помощью sbrk(), не может быть легко отдана снова (только в особых случаях, и, насколько я знаю, glibc даже не пытается). Память, выделенная с помощью mmap(), может быть возвращена с помощью munmap().

Если вы зависите от возможности вернуть память ОС, вы можете использовать mmap() напрямую вместо malloc(); но это станет неэффективным, если вы выделите много маленьких блоков. Возможно, вам потребуется реализовать собственный распределитель пула поверх mmap().

person Jens Kilian    schedule 21.08.2012
comment
+1 за ммап. Я не думал об этом. Но если требуется много небольших фрагментов, я бы выбрал специальный процесс. - person Axel; 21.08.2012
comment
free может иногда освобождать память для ядра через munmap. Я думаю, что он делает это для достаточно больших зон памяти. - person Basile Starynkevitch; 21.08.2012
comment
Итак, вопрос в том, как мне убедить glibc() вызывать munmap? Есть ли какая-то опция компилятора, системный вызов, что-то еще, что меняет поведение? - person ModdyFire; 21.08.2012
comment
Возможно, это поможет. - person Jens Kilian; 21.08.2012

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

Если для обработки требуется несколько ГБ памяти, пусть ваш сервер дождется запроса, а затем создаст новый процесс для обработки данных. Вы можете общаться со своим сервером с помощью каналов. Когда обработка завершена, верните результат и завершите порожденный процесс.

person Axel    schedule 21.08.2012

Я предполагаю, что способ выделения памяти (и, возможно, возврата ОС) находится в libc. Это может быть связано с используемым вами стеком языка программирования/библиотеки.

Я предполагаю, что glibc вернет нефрагментированную память в верхней части кучи. Ваш процесс может выделить 10 МБ данных, которые он будет использовать все время. После этого будет выделено 500 МБ данных, которые используются в обработке. После этого выделяется крошечный фрагмент данных, который сохраняется даже после обработки (может быть результатом обработки). После этого выделяется еще 500 МБ Распределение памяти:

|Использовано 10 МБ|Обработка 500 МБ|Результат 1 МБ|Обработка 500 МБ| = всего 1011 МБ

Когда 1000 МБ освобождаются, структура памяти

|использовано 10 МБ|освобождено 500 МБ|результат 1 МБ|освобождено 500 МБ| glibc теперь может возвращать память в конце... |использовано 10 МБ|освобождено 500 МБ|результат 1 МБ| = 511 МБ используется, из них используется только 11 МБ.

Я предполагаю, что это то, что происходит, вам нужно будет провести дальнейшее исследование (на ум приходят отдельные пулы памяти), чтобы убедиться, что вся память будет освобождена

person DThought    schedule 21.08.2012

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

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

person Brian Agnew    schedule 21.08.2012
comment
Спасибо, но спавн мне не подойдет. И я использую С++. (Я должен был сказать это раньше) - person ModdyFire; 21.08.2012

Вы должны посмотреть, как работает пагинация. Вы не можете вернуть память, если она меньше, чем getpagesize().

person Community    schedule 06.02.2013