Подтвердить что ты не робот

Возможно ли полностью избежать фрагментации кучи?

Например, если освобождение динамической памяти всегда выполняется в противоположном направлении к распределениям. В этом случае гарантируется, что куча не будет фрагментирована?

И с теоретической точки зрения: существует ли реалистичный способ для нетривиального приложения управлять памятью, чтобы полностью избежать фрагментации кучи? (После каждого изменения атома в куче, куча до сих пор не подвержена обрыву?)

4b9b3361

Ответ 1

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

Да. Выделите все в стеке. Это может показаться советом совершенства, но я сделал это в нетривиальных программах.

РЕДАКТИРОВАТЬ Для абсолютной ясности и избежания сомнений я сказал "стек". Благодаря этому я имею в виду автоматическое хранилище, то есть локальный стек переменных, а не класс стека. Некоторые из комментариев об этом в основном тупые.

Ответ 2

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

Просто анекдотический бит истории:
Старый MacOS (эпоха pre-MacOS-X!) Использовал так называемые дескрипторы для объектов памяти: указатели в списки указателей на реальные области памяти. Это имело то преимущество, что ОС могла перемещать объект памяти, изменяя его указатель в таблице; любые ссылки на рукоятку останутся нетронутыми. Но вам приходилось блокировать дескриптор каждый раз, когда вы хотели получить доступ к своим данным через системный вызов. Следует отметить, что это было до того, как multicore стал основным, поэтому не было реального concurrency. При использовании многопоточного приложения каждый раз нужно было бы блокировать дескрипторы, по крайней мере, если бы другим потокам разрешалось звонить в систему. Повторяю, есть причины, по которым в MacOS-X не справились ручки...

Ответ 3

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

Но в любом практическом приложении, которое решает какую-то реальную проблему, это маловероятно. Конечно, любое использование std::string или std::vector почти наверняка выделяет/освобождает память "неупорядоченными способами".

Если у вас есть сценарий, где фрагментация кучи является потенциальной проблемой, почти наверняка лучше использовать решение, которое уменьшает/устраняет это (например, фиксированное распределение ведра, отдельные кучи для различных типов распределений - это всего лишь два из MANY различные решения).

Ответ 4

Можно разбить кучу на области, где допускается выделение памяти определенного размера. Таким образом, ваша куча не будет разбита на разделы, но у вас будут накладные расходы на память и риск исчерпать свободные куски, но иногда это оправдано (скажем, когда программное обеспечение должно оставаться в сети 24/7/365). из 2 или наиболее типично используемых размеров для приложения или только новые области памяти, выделенные по требованию при первом распределении размера N до некоторого разумного N.

Он будет выглядеть примерно так (* - выделенный блок, - свободный блок)

 size=1: [*][ ][*][*][*][ ][ ][ ][ ][ ]
 size=2: [  ][  ][  ][**][**][  ][  ][  ]
 size=4: [    ][****][    ][    ][    ]
 size=8: [        ][        ][        ]
 ....

Визуализатор, который я написал некоторое время назад, когда экспериментировал с темой: http://bobah.net/d4d/tools/cpp-heapmap

Ответ 5

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

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

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

Ответ 6

Как упоминалось выше delnan, другой проблемой является фрагментация фрагментов виртуальной памяти, которая может возникать, когда имеется много распределений и освобождение памяти. Приложение Windows.net использует схему сбора мусора .net для переустановки выделенной памяти, но это приводит к паузе в текущей программе. Я не уверен, сколько времени занимает каждая пауза.

Ответ 7

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

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