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