作用域退出时字符串数组未被释放

11

我的应用程序存在严重的内存泄漏问题,因此我设置了这个极简单的解决方案来测试当String数组超出范围时会发生什么...

我知道旧版TextString的String实现缺少析构函数,但是这个当前的实现似乎已经解决了这个问题。

我正在使用这个MemoryFree库 (请注意,这个链接代码现在已根据本问题的接受答案进行修复)。

该代码检查两种情况:在两个不同的函数中分配char数组和string数组,以强制在两者上执行作用域退出。

#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年之后,差异为0
STRING:1645年之前,1309年之后,差异为336

为什么String数组分配没有从内存中清除?


1
当字符串数组较小(例如仅包含10个元素)时,会得到什么结果? - ouah
有趣的事情: 50个元素=差异136, 25个元素=差异36, 10个元素=差异0 - Silas Hansen
你使用的Arduino软件版本是什么(0023,1.0,...)? - Matthew Murdoch
我正在使用软件版本1.0,在Arduino UNO R3板上运行,控制器为ATmega328。 - Silas Hansen
3个回答

4

在测试String类时,我发现Arduino 1.0之前的版本存在内存处理问题(参见论坛帖子)。

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's 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;
}

谢谢!原来是freeMemory函数报告了错误的值。使用您提供的方法,可以得到正确的结果,并且不会发生泄漏。 - Silas Hansen
很高兴知道Arduino 1.0库没问题。我会更新答案。谢谢! - Matthew Murdoch
我已经根据这个被接受的答案,在Arduino网站上更新了MemoryFree.cpp库的代码(http://arduino.cc/playground/Code/AvailableMemory)。 - Matthew Murdoch

0
如果您查看Arduino源代码,您可能会遇到文件“.\arduino-1.0\hardware\arduino\cores\arduino\WString.cpp”。在这个文件中,我注意到String没有默认(无参数)构造函数。也许这就是问题所在?虽然有些怀疑,但是源代码应该会有所帮助。祝你好运。

我已经在研究那个文件了,主要是为了验证它是否有析构函数。我将测试一下使用值初始化的数组是否有任何区别。 - Silas Hansen

0
注释掉 String s[100]; 这一行,看看是否会得到不同的结果。看起来你在 setup() 函数中的字符串操作导致了你看到的内存分配,而不是在 AllocateStrArr() 中声明本地字符串数组。你可以查看 WString.cppWString.h,以查看已重载 operator+,因此每次调用 String() 或使用 + 进行连接都可能创建另一个对象。

注释掉那一行会使其diff为0。 - Silas Hansen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接