Страницы

Поиск по вопросам

воскресенье, 24 ноября 2019 г.

Фрагментация памяти


Как известно, сборщик мусора в C# (точнее, в CLR) время от времени проводит чистк
оперативной памяти, освобождая память, занятую переменными, которые больше не используются. Кроме этого он также производит дефрагментацию памяти, "уплотняя" кучу. 

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

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

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


Ответы

Ответ 1



Вопрос очень хороший. И тема интересная и важная. Однако, мне кажется (может быть просто кажется), что Вы путаете две вещи, точнее, два уровня фрагментации памяти. Памят может быть фрагментирована на уровне физической памяти. В системах с виртуальной моделью памяти (а таких сейчас подавляющее большинство) это не проблема, так как даже сильно фрагментированная реальная память будет просто спроецирована на последовательное виртуальное адресное пространство процесса. Другое дело, если фрагментация происходит на уровне виртуальной памяти. Это може запросто произойти в программах на С или С++, где происходит многочисленные выделени и удаления небольших фрагментов памяти. Это может привести к сильной утечки памяти (хотя в коде вся выделенная память освобождается!) и, возможно, к исчерпанию всей системной памяти. Но тут уже всему настанет кердык, если система такие ситуации не отслеживает и не выгружает "прожорливые" процессы.

Ответ 2



Да, такое возможно. Причем часто возникает в нагруженных приложениях. Для решения этой ситуации есть много решений. Например кастомные аллокаторы. Пуст в приложении нужно выделять много раз память под мелкие объекты. Кастомный аллокато выделяет память немного большего размера (округляя до кратного 2 в степени). Аллокато при старте выделяет один большой объем памяти и разбивает его на участки для 2в8, для 2в10 (и так далее). При правильном подходе аллокатор хоть и будет тратить больше памяти, но не будет фрагментации. Деструктор не возвразщает память назад системе, а просто помечает как свободной.

Ответ 3



Вопрос почему-то опять вызвал интерес. Вот взял и попробовал. Всегда приятно узнавать что-то новое. Краткий отчет: Оказалось, что в убунте, если soft limit не установлен, то вместо ожидаемого ENOMEM мы получаем SIGKILL от ядра. На 64-бит виртуалке с гигом ОЗУ и 2.5 гигами свопа при malloc(1000) этот процес продолжается всего-то 1.5 минуты !!! (Так что будем считать, что я просто пошутил, утверждая, "что так долго не живут"). После установки лимита на виртуальную память (800 мегов), malloc все-таки стал возвращать 0 и я проверил вопрос о фрагментации. Действительно, освободив 80 мегов realloc-ом "по месту" (уменьшая каждый блок н 100 байт), не смог выделить 1000 байт malloc-ом, а по 50 байт удалось получить, как не сложно догадаться, только половину из освобожденной ранее памяти. (Если тестовая программка кому-то интересна, то напишите, завтра вставлю в дополнение ответа.)

Ответ 4



Ожидаемой Вами дефрагментации с кусками меньшими 4К добиться, наверное, можно, но такое время программы не живут. живут, живут. Гигабайты свопа -- величина не бесконечная. Хотел ответить комментарием, но не хватило места. Для того, чтобы исчерпалась виртуальная память, в 64-битных системах в теории требуются эксабайты (на практике существует поддержка до пета-, но в общем случае - тера-). А если учесть, что реальный объем физической памяти редко превышает объем в 32Г (возьмем к примеру 4 слота по 8Гб), то простое распределение (не резервирование) памят до теоретических (и даже до практических) пределов (да еще и фрагментированное по каким-то там килобайтам) будет занимать столько времени на операциях свопирования, что требуемый для этого uptime любой 64-битной системы не уложится и в нескольких десятках лет (могу ошибаться в порядках). Так что @avp, скорее прав -- врятли :) Скорее случится одно из двух: Деградирует операционная система. Закончится дисковая память, выделенная под своп. Комментарии: А чем отличается распредление от резервирования? В комментариях уже объяснял. Резервирование - это резервирование виртуальных страни под любые нужды приложения, но минуя операцию выделения на этот диапазон физических страниц. Да и разве своп может вырасти до 18 Эксб? Имхо, предел - пара Гб (зависит от настроек ОС, конечно. у меня 2 гб стоит.) Свопу просто не дадут вырасти до таких пределов, тоже уже обсуждали в комментах. p.s.: у меня комментарии здесь уже кончились, так что, если будут вопросы - буд комментировать в ответе, обращайте на него внимание, пожалуйста.

Ответ 5



Вопрос очень хороший спросили, и его оживлённо читают. Мой ответ скорее не ответ, а вопрос в продолжение темы почему-то уже принятого вопроса. Возможно ли померять степень фрагментации данных в памяти своего приложения? Чужого? Может есть возможность вообще построить наглядную карту памяти с Ее дефрагментацией? Помнится во времена xp было пару программ, которые занимались тем, что втихую дефрагментировал молча данные в ОЗУ и отправляли их в своп при длительном не использовании, к сожалению пример названий сейчас этих двух приложений уже и не вспомню.

Комментариев нет:

Отправить комментарий