【问题标题】:memory leak (Valgrind) on a grid of points点网格上的内存泄漏(Valgrind)
【发布时间】:2012-05-06 08:00:40
【问题描述】:

我正在使用 mapPixel 的概念创建一个地图网格(一个 2D 离散点),一个类:

class MapPixel
{
    friend class Map;
protected:
    int x;
    int y;
    float height;
    float vegetation;

    std::vector<const MapPixel*> neib;
...methods declaration, default constructor/destructor

其中 neib 是指向其他 MapPixels 的指针列表,与 then 相邻。

我用的是方法

void MapPixel::addNeib(const MapPixel* neib_)
{
    neib.push_back(neib_);
}

添加指向 neiber 像素的指针以构建图形(由于边界的 neib 比中心像素少,因此此列表取决于大小)。

我的程序是有一个带有成员的类 Map

MapPixel **pixels;

在构造函数 Map::Map() 中使用

pixels = new MapPixel*[width];
for (int i = 0; i < width; i++)
    pixels[i] = new MapPixel[height];

我使用 MapPixel::addNode() 方法构建网络(例如)

pixels[i][j].addNeib(&pixels[i][j+1]);

在 Map::~Map() 中,我按相反的顺序删除 MapPixel(不删除 neib 以避免双重释放):

for (int i = 0; i < width; i++)
    delete pixels[i];
delete pixels;

Valgrind 说有几个像这样的大内存泄漏:

2,509,088 bytes in 39,205 blocks are possibly lost in loss record 4,071 of 4,071
  in MapPixel::addNeib(MapPixel const*) in Source/mappixel.cpp:52
  1: malloc in vg_replace_malloc.c:266
  2: operator new(unsigned long) in /usr/lib/libstdc++.6.0.9.dylib
  3: __gnu_cxx::new_allocator&lt;MapPixel const*&gt;::allocate(unsigned long, void const*) in ...
  4: std::_Vector_base&lt;MapPixel const*, std::allocator&lt;MapPixel const*&gt; &gt;::_M_allocate(unsigned long) in stl_vector.h:131
  5: std::vector&lt;MapPixel const*, std::allocator&lt;MapPixel const*&gt; &gt;::_M_insert_aux(__gnu_cxx::__normal_iterator&lt;MapPixel const**, std::vector&lt;MapPixel const*, std::allocator&lt;MapPixel const*&gt; &gt; &gt;, MapPixel const* const&amp;) in vector.tcc:271
  6: std::vector&lt;MapPixel const*, std::allocator&lt;MapPixel const*&gt; &gt;::push_back(MapPixel const* const&amp;) in stl_vector.h:608
  7: MapPixel::addNeib(MapPixel const*) in mappixel.cpp:52

与第 52 行有关:

neib.push_back(neib_);

有人明白吗?现在我对是否可以使用 std::vector 来构建像素的 neibs 失去信心。

【问题讨论】:

  • 在可能的内存泄漏问题中,您应该告诉我们您如何分配(新)和如何释放(删除)...这里缺少一部分。例如。你按相反的顺序删除MaxPixel,ok;但你是像素本身吗?我想答案是肯定的,并且给定的答案适用。不过,最好有完整的新/删除代码来查看!
  • 添加了删除代码。我说这不是因为 Valgrind 不会抱怨新像素/新像素 [i],这就是为什么我认为问题不存在。
  • 不应该是delete[]吗?
  • 我对 C++ 很生疏,我怀疑delete[] 是否在元素上调用析构函数,确实如此,但这里我们有普通指针,而不是 MapPixel 对象或对它们的引用,所以看起来那:你释放了你动态创建的 MapPixel 对象,但没有正确释放动态创建的指针数组(指向此类对象)。

标签: c++ memory-leaks valgrind


【解决方案1】:

请注意,valgrind 说“可能丢失”,而不是“肯定丢失”。区别很重要。具体含义见here

该错误与vector&lt;&gt; 实现代码分配的块有关,很可能随着vector 的增长而调整包含元素的内存块的大小。如果您分配 MapPixel 的实例并忘记释放它们,您可能会得到这些,因为包含 vector 将无法释放其内存,但您也会收到有关您自己的代码的错误。

除非!当你释放pixels 数组时,你使用的是delete[] 还是delete

更新:您正在使用delete。您需要使用delete[]。这确实是内存泄漏。使用new[] 分配的任何内容都必须使用delete[] 释放,否则只会为第一个元素调用正确的析构函数(即使是编译器自动生成的析构函数)。

【讨论】:

  • +1 关于像素的问题...刚刚完成关于缺乏细节的评论... :)
【解决方案2】:

正如已经提到的另一个答案,内存泄漏很可能是由错误的delete 运算符引起的。在构造函数中,您使用operator new[] 创建一个数组数组:

pixels = new MapPixel*[width];
for (int i = 0; i < width; i++)
  pixels[i] = new MapPixel[height];

您需要使用相应的 array-delete operator delete[] 释放数组的内存:

for (int i = 0; i < width; i++)
  delete [] pixels[i];
delete [] pixels;

但是,我建议您改用嵌套的std::vector 作为像素矩阵。这样您就可以免费获得内存管理。

std::vector<std::vector<MapPixel> > pixels;
// in constructor something like:
pixels.resize(width, std::vector<MapPixel>(height));
// nothing to do in destructor

对于您的邻居,我不会使用 std::vector,而是使用普通的 MapPixel *neib[8];(假设为摩尔社区)或更确切地说是 std::array&lt;MapPixel*, 8&gt; neib;。但我不知道您对这个项目可能还有什么其他要求。

除了内存管理,使用 STL 容器还有其他好处,例如方便的成员函数,它们不会衰减为指针,仅举几例。

【讨论】:

  • "(因为边框的 neib 比中心像素少,所以这个列表取决于大小)" 这就是我没有使用 MapPixel *neib[8] 的原因。此外,分配只是在开始,一旦分配,std::vector 就是常量访问。关于答案,我会检查它是否修复了错误。
  • @J.C.Leitão 不,你获得了一些locality,即数组的内存直接集成在对象本身中,而向量元素的内存驻留在堆的其他地方。但考虑这些事情可能更像是过早优化。
  • 可能丢失了 2,509,088 字节,我认为在代码中添加熵还为时过早... xD
最近更新 更多