c++ - 为什么在分配/取消分配一些小对象后内存不可重用?

标签 c++ vector memory-leaks stl crt

在我们的一个项目中调查内存链接时,我遇到了一个奇怪的问题。不知何故,当父容器超出范围时,分配给对象的内存(对象的 shared_ptr vector ,见下文)没有完全回收,除了小对象外不能使用。

最小示例:当程序启动时,我可以毫无问题地分配一个连续的 1.5Gb block 。在我稍微使用内存之后(通过创建和销毁一些小对象),我不能再进行大块分配。

测试程序:

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class BigClass
{
private:
    double a[10000];
};

void TestMemory() {
    cout<< "Performing TestMemory"<<endl;
    vector<shared_ptr<BigClass>> list;
    for (int i = 0; i<10000; i++) {
        shared_ptr<BigClass> p(new BigClass());
        list.push_back(p);
    };
};

void TestBigBlock() {
    cout<< "Performing TestBigBlock"<<endl;
    char* bigBlock = new char [1024*1024*1536];
    delete[] bigBlock;
}

int main() {
    TestBigBlock();
    TestMemory();
    TestBigBlock();
}

如果在循环中使用带有 new/delete 或 malloc/free 的普通指针而不是 shared_ptr,问题也会重复出现。

罪魁祸首似乎是在 TestMemory() 之后,应用程序的虚拟内存停留在 827125760(无论我调用它多少次)。因此,没有足够大的免费 VM 区域来容纳 1.5 GB。但我不确定为什么 - 因为我肯定会释放我使用的内存。 CRT 是否进行了某种“性能优化”以最大程度地减少操作系统调用?

环境是 Windows 7 x64 + VS2012 + 没有 LAA 的 32 位应用

最佳答案

很抱歉发布另一个答案,因为我无法发表评论;我相信其他很多人都非常接近答案:-)

无论如何,罪魁祸首很可能是地址空间碎片。我了解到您在 Windows 上使用 Visual C++。

C/C++ 运行时内存分配器(由 malloc 或 new 调用)使用 Windows 堆来分配内存。 Windows 堆管理器有一项优化,它会保留一定大小限制以下的 block ,以便在应用程序稍后请求类似大小的 block 时能够重用它们。对于较大的 block (我不记得确切的值,但我猜它大约是一兆字节),它将完全使用 VirtualAlloc。

其他具有许多小分配模式的长时间运行的 32 位应用程序也有此问题;让我意识到这个问题的是 MATLAB - 我使用“元胞数组”功能基本上分配了数百万个 300-400 字节的 block ,即使在释放它们之后也恰恰导致了地址空间碎片问题。

解决方法是使用 Windows 堆函数(HeapCreate() 等)创建私有(private)堆,通过它分配内存(根据需要将自定义 C++ 分配器传递给容器类),然后在需要时销毁该堆你想要回内存——这也有一个令人愉快的副作用,那就是与在一个循环中删除()无数个 block 相比,速度非常快..

回复。 “内存中剩余的内容”首先导致问题:“内存中”本身没有任何内容,更多的是释放 block 被标记为空闲但未合并的情况。堆管理器有一个地址空间的表/映射,它不允许您分配任何会迫使它将空闲空间合并到一个连续 block 中的东西(大概是一种性能启发式)。

关于c++ - 为什么在分配/取消分配一些小对象后内存不可重用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19653597/

相关文章:

c++ - boost::assign 与 std::map

c++ - dev c++编译器错误

java - Blazeds 中是否有 Flex' Vector 到任何 Java 类型的直接映射?

c++ - 显示字符串子 vector C++

delphi - 为什么释放不为空的TList <Int64>不会导致内存泄漏?

c++ - %llx 格式说明符 : invalid warning?

c++ - 如何将方法声明为 "friend"?

r - 获取向量的最后一个元素

java - 带有临时类实例的垃圾收集

objective-c - 关于ios的内存管理