Разрешение размещения кучи в недолгой области видимости для обеспечения свободы фрагментации памяти
Мы используем C++ во встроенной системной среде и в основном не хотим какого-либо динамического выделения памяти (см., Например, " Ресурсы для управления памятью во встроенном приложении" по причинам, почему мы этого не делаем). Тем не менее, мы не хотим обходиться без некоторых приятных функций на основе C++, таких как контейнеры STL и std::string. Для первого мы зарезервировали бы определенный размер при инициализации и не позволяли бы контейнеру превышать его емкость. Что касается последнего (std::string), я немного скептически относился к тому, как использовать их "безопасно", поскольку они иногда выделяют память в куче.
Однако я обнаружил обстоятельства, при которых кажется целесообразным использовать std::string (и вообще другие объекты, выделяющие кучу): я бы выделил сам объект в стеке (в определенной области, ограниченной {}
как я говорю из C++) и позволяю им выделять кучу, если они фактически освобождают всю свою зарезервированную память, когда выходят из области видимости.
Я понимаю, что этот метод определенно не гарантирует свободу фрагментации памяти, но я чувствую, что если имеющаяся область действия недолговечна, это фактически приводит к непрерывной свободной памяти после ее окончания.
У меня также были сомнения, что может возникнуть проблема, когда несколько задач, совместно использующих одну и ту же кучу, но все же свободная память, должны быть непрерывными в конце, при условии, что все имеющиеся области под рукой недолговечны (не блокируйте, например). В качестве альтернативы для меня было бы приемлемо, что только одной задаче разрешено выделять память в куче, а другим - нет, если это действительно имеет значение.
Допустимо ли мое предполагаемое использование объектов, выделяющих кучу? Есть ли у кого-то другая стратегия для (частично) включения динамического выделения памяти без риска фрагментации памяти?
2 ответа
В прошлом мы выполняли все виды динамического выделения памяти в стиле C++ в тесных встроенных системах. Вы просто должны следовать нескольким правилам и быть осторожными при использовании краткосрочных и долгосрочных буферов. Во-первых, пулы памяти - ваш друг, как говорится в статье.
Кроме того, для всех небольших (<64 байт) выделений, которые C++ любит делать, помогая с парами и структурами управления, необходима схема выделения модулей - не только для контроля фрагментации, но и для производительности. Распределитель единиц памяти предварительно выделяет количество единиц памяти одинакового размера (скажем, 64 байта) и помещает их в свободный стек. По мере выделения памяти вы извлекаете их из свободного стека и возвращаете их. Поскольку все размеры одинаковы, у вас есть только внутренняя фрагментация по размеру блока. Поскольку вам не нужно объединять память, когда это сделано, выделение и освобождение занимает O(1) раз.
Некоторые другие правила: Если вам нужно сделать динамическое распределение, которое будет долгосрочным, не располагайте какие-либо краткосрочные распределения до него. Сначала выделите большой буфер, а затем самый маленький, чтобы память не рассеялась. Другая система будет заключаться в том, чтобы размещать долгосрочные ассигнования на заднем плане и краткосрочные на переднем. У нас также был успех с этим.
Вы также можете использовать несколько куч (пулов) для разделения различных типов распределений. Если у вас есть что-то, что создает целую кучу краткосрочных выделений в одном разделе кода, в то время как другой раздел следует другому шаблону, выделите им другую кучу.
Все вышеперечисленное, если его тщательно соблюдать, будет либо предотвращать, либо ограничивать фрагментацию. Другое решение заключается в использовании перемещаемой системы выделения памяти, в которой поток с низким приоритетом может переупорядочивать память, чтобы поддерживать ее непрерывной со временем. Я видел, что это также делалось несколько раз - торгуя с небольшой производительностью за 0 долгосрочной фрагментации.
alloca
также может помочь, но если вы не будете следовать методам предотвращения фрагментации памяти, вы просто прекратите разбрасывать свой стек - и, поскольку это имеет тенденцию быть более ценным ресурсом во встроенной среде, это может быть не очень хорошей идеей.
Вы можете считать не очень популярным alloca
функция для размещения переменных размера переменных в стеке. Таким образом, нет фрагментации, но вы можете столкнуться с переполнением стека, если вы используете alloca
для больших переменных.
Редактировать: некоторая информация о alloca из документации библиотеки GNU C.