在嵌入式应用程序中释放内存无助于减少虚拟存储

Posted

技术标签:

【中文标题】在嵌入式应用程序中释放内存无助于减少虚拟存储【英文标题】:Freeing memory in an embedded application won't help in reducing virtual storage 【发布时间】:2015-04-08 10:06:30 【问题描述】:

我的嵌入式 Linux 系统有 28K RAM 和 31.6M 的闪存,文件系统安装在上面。我开发了一个需要在嵌入式 Linux 中连续运行的应用程序。问题是应用程序的虚拟存储大小(top 命令中的 VSZ)不断增加,直到系统崩溃。如果我理解正确并纠正我如果我错了,VSZ 应该根据程序完成的内存分配而变化,如果内存被释放,它应该回到之前的值并保持不变。

程序在源代码中使用如下声明:

1- 结构指针向量:

vector<Struct*> someVector; // Struct pointers are initialized with new

// After the previous vector has some values, it is freed like this
for(unsigned i = 0; i < someVector.size(); i++)

    delete someVector[i];

someVector.clear();
vector<Struct*>().swap(someVector);

2- 字符串向量,与之前相同,但不是循环内的deletesomeVector[i] = "";

3-char*

char* somePointer = (char*) malloc(100);
free(somePointer);
somePointer = NULL;

4- 有时我使用 fstream* 初始化 new 我认为这不会有任何区别,因为 C++ 对象一旦超出范围就会被销毁。

5- char** array = new char*[100];delete[] array 删除。我在内部元素上使用了delete,但valgrind 给了我Mismatched free/delete 或类似的东西。

注意:来源很大,这些是应用程序使用最多的数据结构。如果还有什么可疑的地方我会更新的

我编译应用程序并使用 SSH 将其移动到嵌入式设备,我运行应用程序并监控 top 命令输出;应用程序进程的VSZ值不断增加,直到11K这样的大值,系统崩溃。我很确定我所做的每一次分配都有相应的免费机制(free, delete 等)。

valgrind ./myApplication 工具甚至没有显示警告消息,我应该假设分配和释放是安全完成的吗?

感谢任何帮助。我可以立即获取所需的任何信息。提前致谢!

2015 年 8 月 4 日更新:增加应用程序 VSZ 的过程是收集数据并将它们连续存储在闪存上的文件中,有一种机制可以检查是否超出大小,但我现在不使用它。这有关系吗?

【问题讨论】:

您的意思可能是 28M RAM,而不是 28K? “有时我使用 fstream* 初始化 new 我认为这不会有任何区别,因为 C++ 对象一旦超出范围就会被销毁。” - 除非你明确地调用delete,或者使用智能指针。 “我在内部元素上使用了 delete 但 valgrind...” - 如果它们被分配了 new char[],你确实应该使用 delete[]:你确定你只是删除数组的子集你填充的元素呢?你最好使用vector&lt;std::string&gt;,通常情况下会自行释放。不需要2个someVector[i] = ""; @amdn 也可能是 28Kb。毕竟是嵌入式系统。我正在使用具有 192Kb RAM 的系统,并且永久占用了 32Kb。我的朋友说无论如何这个处理器已经足够了,所以我觉得完全有可能。 @luk32,可能是,但后来 OP 说 VSZ 一直在增加,直到像 11K 这样的大值 - 即使对于嵌入式来说也似乎很小。 valgrind 不抱怨#2 吗?这显然是内存泄漏,除非向量 elemenst 先前指向的字符串是静态的。对于#5,您应该使用delete[] 删除内部元素,而不是delete 【参考方案1】:

绝对没有理由期望 delete/free 将虚拟内存返回给系统 - 他们通常将其保留在进程中以供以后重用。一些分配模式会导致增长,即使由于免费存储碎片而没有泄漏。

第一步仍然是确保您没有泄漏,使用 valgrind 并在可能的情况下在智能指针控制下移动这些动态分配,并使用 valgrind 的 massif 工具查看您分配的内存的去向(这将有助于识别像 luk32 提到的问题)。

如果仍然需要,下一步是使用专门的分配器:固定大小的对象分配器(消除碎片并最大限度地减少开销)、具有众所周知的生命周期的对象(无论大小)的竞技场分配器,以及基于 mmap 的用于将 VM 显式、确定性地返回给操作系统的后备存储。

请注意,您确实有一个可用的内置 arena 分配器 - 它用于具有自动作用域的局部变量。使用它而不是 newing 一切 - 在适用的情况下 - 让生活变得更加简单。

【讨论】:

【参考方案2】:

someVector[i] = ""; 这并不一定意味着内部缓冲区将被修剪。可以用capacity()查看,c++11中有shrink_to_fit,其他方法好像不行。

#include <iostream>
using namespace std;

int main() 
    string s("Some example string. Let's give it few more bytes.");
    cout << s.size() << '\n';
    cout << s.capacity() << '\n';
    s = "";
    cout << s.capacity() << '\n';
    s.clear();
    cout << s.capacity() << '\n';
    s.resize(0);
    cout << s.capacity() << '\n';
    s.shrink_to_fit();
    cout << s.capacity() << '\n';
    return 0;

为除shrink_to_fit 之外的所有方法提供50,在ideone 上:http://ideone.com/nwIzYz。 valgrind 也不会将其报告为“丢失”。因此,在您删除这些对象之前,它们将保留在其内部缓冲区中。如果您在某个全局范围内使用它,然后尝试通过这些调用来释放内存,那么它很可能无法正常工作。

【讨论】:

shrink_to_fit 需要 C++11 而我的编译器不支持。 我只是说如果计算 someVector[i] = ""; 会立即释放内存你错了。如果你想释放它,你需要使用shrink_to_fit或者删除字符串,someVector.clear()应该这样做。我不知道你的情况,所以我不能给出具体的答案。你需要得出结论,我相信我已经提供了足够的信息。例如,如果您将向量留在周围,以供进一步重复使用,它仍将占用空间。但是不知道你是怎么用的。【参考方案3】:

New/delete 内部使用malloc/free。这两个函数都是内核函数的包装器,例如 mmapbkr/sbrk(在 linux 上)。这些函数跟踪页面以在完成大量小分配的情况下优化内存碎片。

当您调用free 时,这些页面可能不会返回给操作系统,就像您向操作系统查询内存时一样,操作系统不会给您内存,直到内存被使用或看起来像您会用的。

如果你想将内存归还给操作系统,在linux上,你可以使用malloc_trim。

我在 c++03 编译器上使用这种方法来缩小以适应。

template<typename T, class Allocator>
void shrink_capacity(std::vector<T,Allocator>& m)

    std::vector<T,Allocator>(m.begin(),m.end()).swap(m);

【讨论】:

你的意思是和 shrink_to_fit 一样吗?什么是分配器?

以上是关于在嵌入式应用程序中释放内存无助于减少虚拟存储的主要内容,如果未能解决你的问题,请参考以下文章

如果/啥时候释放的堆内存被回收?

STM32 内存分配解析及变量的存储位置

Linux下swap耗尽该怎么办?如何释放swap

虚拟环境和嵌入 Python

C语言中内存分配 (转)

UCOSIII存储管理