Утечка памяти, вызванная конкатенацией строк
В этой ссылке, раздел «Память, память, память» было сказано, что последовательное выполнение множества конкатенаций строк, например: многократное использование оператора+(), приведет к фрагментации памяти, что может привести к тому, что общая куча не будет использована полностью.
Альтернатива, которую я предлагаю, — использовать strcat() с буферами символов и вообще не использовать строки, а затем преобразовать результирующий буфер символов в строку, если в конце требуется строка? Будет ли это спасением от фрагментации кучи?
Пояснение: я заранее укажу верхнюю границу буфера символов, как если бы я знал максимальный необходимый/который будет использоваться размер.
@YoussefDir, 👍0
Обсуждение2 ответа
Если вы установите размер буфера, позволяющий хранить вашу окончательную строку, то да, этот метод позволит избежать фрагментации, вызванной функцией конкатенации String
.
Обратите внимание, что String
внутри хранит строку как массив символов в стиле C; это процедура конкатенации, которая динамически выделяет и освобождает память, вызывая фрагментацию.
Интересно, если просто выделяя и освобождая память, то буфер символов тогда вызывает фрагментацию! (Я имею в виду, что я буду его освобождать) Но поскольку выделенный размер соответствует требованиям, то это все равно намного лучше, чем конкатенация строк. Согласны?, @YoussefDir
Объект String выделяет новый фрагмент памяти, достаточно большой для размещения полученных объединенных данных, копирует обе исходные строки в новую память, а затем освобождает старую память (первой строки). Это означает, что есть свободный бит памяти, который теперь меньше, чем любые последующие потребности для будущей конкатенации рассматриваемой строки, так что этот бит свободной памяти *может* снова использоваться какой-то другой функцией, но он не будет снова использоваться строкой, с которой выполняется конкатенация., @jose can u c
Я думаю, что стандартные манипуляторы строк C лучше подходят для небольших микро, чем объект String
, хотя они и сложнее в использовании. Оба метода работают во многих случаях, но вы должны знать, как они работают, чтобы вы могли понять при отладке проблем., @jose can u c
Ваш метод уменьшит фрагментацию. Но понадобится ли вашему приложению сделать это один раз? Несколько раз? Должно ли оно работать вечно? Если ваше приложение часто использует пары malloc()/free() или new/delete, оно будет фрагментировать кучу, вызывая ее рост и в конечном итоге вызывая коллизию стека. Дело не столько в том, что Arduino является устройством с ограниченными ресурсами; в конечном итоге это произойдет на любом постоянно работающем устройстве с фиксированным объемом памяти.
Если вы можете установить верхнюю границу требований к буферу, то предварительно распределите пул буферов глобально, локально как статический уровень файла или локально или в куче (с помощью malloc() [1]) — массив «достаточно больших» буферов, достаточного для вашего приложения. Затем всегда выделяйте/отменяйте выделение одного из них, и вы избежите фрагментации. Ваше приложение (или библиотека обработчиков буферов) должно будет отслеживать, какие буферы свободны, а какие используются, так же, как это делают системные распределители. Но в обмен на запись таким образом вы получаете возможность выделять и освобождать память навсегда, без фрагментации. Каждый запрос и каждый возврат имеют одинаковый размер, поэтому любой бесплатный фрагмент может заполнить запрос без необходимости увеличения пула.
Мое личное предпочтение — статическое распределение (на уровне файла или глобально видимое), потому что 1) оно поступает из нижней части памяти, что имеет тот же эффект, что и выделение в куче, и 2) оно объявлено как массив массивов, поэтому Синтаксис индексации массива работает без необходимости писать его вручную, как если бы вы использовали malloc() для большого фрагмента.
Примечание[1]: Хотя я предостерег от использования вызовов malloc() и free(), здесь и в других местах сайта, причиной фрагментации является повторное выделение и освобождение из кучи или из нее. Однако в этом случае я предлагаю использовать malloc() для выделения памяти, которая будет сохраняться до конца выполнения программы.
При установке верхней границы, например, char buff[100], я не распределяю ее в стеке? Вместо этого я хочу использовать кучу. Потому что стек в esp8266, над которым я работаю, имеет размер всего 4 КБ, если я не ошибаюсь., @YoussefDir
Мне было непонятно, что пул буферов нужно выделять статически - то есть как глобальный, статический или из кучи (с помощью malloc()). Я обновил свой ответ, чтобы прояснить это., @JRobert
- Какова цель F() и FPSTR() в ESP8266WebServer -> FSBrowser?
- Не удается скомпилировать макрос F() с помощью R "string"
- Создание форматированной строки (включая числа с плавающей запятой) в Arduino-совместимом C++
- Передача функции-члена класса в качестве аргумента
- Arduino Преобразование std:string в String
- Преобразование строки в IP-адрес
- контент» не захватывается
- esp32 Stack canary watchpoint срабатывает
Полезное чтение: [Зло строк Arduino](https://hackingmajenblog.wordpress.com/2016/02/04/the-evils-of-arduino-strings/), @gre_gor