Строковый массив не освобождается при выходе из области

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

Я знаю, что в старой реализации String TextString отсутствовал деструктор, но эта текущая реализация, похоже, имеет его.

Я использую эту библиотеку MemoryFree (Обратите внимание, что этот связанный код теперь исправлен на основе принятого ответа на этот вопрос).

В коде рассматриваются два сценария: распределение массива char и строкового массива в двух разных функциях, чтобы принудительно закрыть область действия на обоих.

#include <MemoryFree.h>

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  int freeBefore, freeAfter;

  //TEST ALLOCATION OF CHAR ARRAY//
  freeBefore = freeMemory();
  AllocateCharArr();
  freeAfter = freeMemory();
  Serial.println("CHAR*: Before " + String(freeBefore)
    + ", After " + String(freeAfter)
    + ", Diff " + String(freeBefore - freeAfter));

  //TEST ALLOCATION OF STRING//
  freeBefore = freeMemory();
  AllocateStringArr();
  freeAfter = freeMemory();
  Serial.println("STRING: Before " + String(freeBefore)
    + ", After " + String(freeAfter)
    + ", Diff " + String(freeBefore - freeAfter));
}

void AllocateCharArr() {
  char s[100];
}

void AllocateStringArr() {
  String s[100];
}

void loop() { /* empty */ }

Вывод:

CHAR *: До 1710 года, после 1710 года, Diff 0
STRING: до 1645, после 1309, Diff 336

Каким образом распределение массива String не удаляется из памяти?

Ответ 1

Я столкнулся с проблемами обработки памяти в версиях Arduino до 1.0 при тестировании класса String (см. форум здесь).

Конструктор String использует realloc внутренне, и это обработка (avr libc) динамической памяти, которая вызывает проблемы (из-за того, что указатель на вершину кучи __brkval не обновляется после free()).

Запустите следующий код, чтобы увидеть эти проблемы в версиях 0023, 0022 и т.д. В Arduino 1.0 код не должен содержать никаких утечек памяти:

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include <HardwareSerial.h>
#include <MemoryFree.h>

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  int freeBefore, freeAfter;

  freeBefore = freeMemory();

  void* buffer = malloc(10);
  if (buffer == 0) {
    Serial.println("Failed to allocate memory");
  }
  free(buffer);

  freeAfter = freeMemory();
  Serial.println("Before " + String(freeBefore)
    + ", After " + String(freeAfter)
    + ", Diff " + String(freeBefore - freeAfter));
}

void loop() {
}

Кроме того, библиотека MemoryFree, которую вы используете, может давать неправильные результаты, поскольку она не учитывает свободный список. Попробуйте обновленную версию MemoryFree.cpp:

extern unsigned int __heap_start;
extern void *__brkval;

/*
 * The free list structure as maintained by the 
 * avr-libc memory allocation routines.
 */
struct __freelist {
  size_t sz;
  struct __freelist *nx;
};

/* The head of the free list structure */
extern struct __freelist *__flp;

#include "MemoryFree.h";

/* Calculates the size of the free list */
int freeListSize() {
  struct __freelist* current;
  int total = 0;

  for (current = __flp; current; current = current->nx) {
    total += 2; /* Add two bytes for the memory block header  */
    total += (int) current->sz;
  }

  return total;
}

int freeMemory() {
  int free_memory;

  if ((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__heap_start);
  } else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
    free_memory += freeListSize();
  }
  return free_memory;
}

Ответ 2

Если вы просмотрите источник Arduino, вы можете столкнуться с файлом ".\arduino-1.0\hardware\arduino\cores\arduino\WString.cpp". В этом файле я заметил, что String не имеет конструктора по умолчанию (без параметров). Возможно, это может быть проблемой? Сомнительный, но в любом случае источник должен помочь. Удачи.

Ответ 3

Прокомментируйте строку String s[100]; и посмотрите, получаете ли вы разные результаты. Похоже, что выделение памяти, которое вы видите, связано с строковыми операциями в вашей функции setup(), а не с объявлением локального массива строк в AllocateStrArr(). Вы можете взглянуть на WString.cpp и WString.h, чтобы убедиться, что operator+ был переопределен, поэтому каждый вызов String() или конкатенации с помощью + может создать другой объект.