使用std::string在共享内存数组中会导致C++内存泄漏问题

8
我有这个类
class LayoutEntry
{
  unsigned int id_;
  string name_;  
  bool isInput_;
};

复制构造函数长这样:
LayoutEntry(const LayoutEntry &other)
        : id_(other.id_),
        name_(other.name_), 
        isInput_(other.isInput_)
    {
    }

该类的对象被放置在另一个类的映射中。

class DataLayoutDescription 
{
    unsigned int sz_;
    set<LayoutEntry, SortByOffset> impl;

    // HERE!!!
    map<unsigned int, LayoutEntry> mapById;

这个类的复制构造函数如下所示:
DataLayoutDescription::DataLayoutDescription(const DataLayoutDescription &other)
    :sz_(other.sz_), impl(other.impl), mapById(other.mapById)
{   
}

现在的问题是:
  • 按照打印出来的方式运行时,每个LayoutEntry都会有一个内存泄漏
  • 如果我在DataLayoutDescription的复制构造函数中删除mapById(other.mapById),那么就不会有内存泄漏了
  • 如果我删除name_(other.name_),,那么内存泄漏也会消失
为什么呢? 编辑 为了测试,我使用BOOST::UnitTest,在最后我得到了一个内存泄漏转储。
C:\wc\05_EAPGit\Debug>EapLibTest.exe --run-test=SharedVectorTest
Running 7 test cases...

*** No errors detected
Detected memory leaks!
Dumping objects ->
{1378} normal block at 0x005815C0, 16 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{1377} normal block at 0x00581580, 16 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{1376} normal block at 0x00581540, 16 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

可能的原因? 我使用这种方法在共享内存区域中保留DataLayoutDescription。

void DataLayoutDescription::Save(LayoutEntry *les, unsigned int maxEntries) const
{
    int n = std::max(impl.size(), maxEntries);
    int i = 0;
    for (DataLayoutDescription::iterator it = begin(); it != end(); ++it)
    {
        les[i] = *it;  // Source of memory leak here???
        ++i;
    }
}

我将解除迭代器并将其副本存储在共享内存区域中的数组中。这样做是否有问题?共享内存将在退出时删除。
进一步跟踪: 类LayoutEntry放置在共享内存区域内的数组中,并包含一个字符串。内存泄漏的原因是该字符串被重新调整大小,从而在堆上分配更多内存。现在我猜测,由于原始内存位于共享内存中,因此这些内存可能不会被释放。这可能是原因吗?接下来,我将尝试删除该字符串,并用固定长度的char数组替换它。
...几分钟后
就是这样。将字符串替换为固定长度的char数组后,内存泄漏消失了。希望这能帮助到某些人。

3
这看起来很完美。你认为有什么迹象表明这里存在内存泄漏? - Kiril Kirov
6
您发布的代码中似乎没有明显的内存泄漏。为什么您认为有内存泄漏呢? - Fred Foo
@schoetbi:如果您运行程序两次,那么转储中的分配号码是否相同? - sharptooth
@nos: DataLayoutDescription 在堆栈上 - schoetbi
@schoetbi:好的,既然你似乎在使用Visual C++,你可以使用这里描述的技术:https://dev59.com/JFHTa4cB1Zd3GeqPUs_F - sharptooth
显示剩余2条评论
4个回答

4

我接受了这个答案,不是因为它对我来说最容易,而是症状看起来像它。我没有做你建议的一切,但由于我也泄漏了16个字节,看起来像它。仍然困扰我的一件事是我安装了SP1。 - schoetbi
如果您已经安装了VS2010 SP1,则请按照链接中我的被接受的答案进行操作(甚至没有注意到这是我写的...)。 - Klaim

3
泄漏的原因是LayoutEntry类存储了一个字符串。修改前的基础对象被放置在共享内存区域中的数组中。在修改字符串后,执行了一次调整大小操作,导致该内存丢失。将字符串替换为字符数组(固定长度)后,内存泄漏问题得到解决。我现在很高兴,但我想知道string类是否有问题,或者是否有办法将自定义分配器放入std :: string中?虽然我不需要这个功能,因为我会使用字符数组,但我只是好奇是否可以这样做,并且我的假设是否正确?
这是修改后的类。
class LayoutEntry
{
  unsigned int id_;
  char name_[128];  
  bool isInput_;
};

感谢大家的帮助!调试内存泄漏的技巧对我帮助很大。


2
C++字符串分配更多的内存来容纳实际字符串。你复制到共享内存中的只是一个指针和长度。实际的字符串从未在共享内存中,所以当你删除共享内存时,你失去了将调用delete[]的指针。 - Zan Lynx

3
假设你说你有内存泄漏时,指的是像Purify或valgrind这样的检查器告诉你有内存泄漏问题,最有可能的情况是你在某些地方泄漏了LayoutEntry对象,而不是直接泄漏string对象。我曾经因为自己的对象被泄漏了,但泄漏被标记为std::string(由valgrind标记),所以很难找到原因。
另一种可能性是它只是检测到了增长。在退出之前,您是否尝试清除您的映射表?

1
如果您在泄漏中有一致的分配编号,可以使用_CrtSetBreakAlloc将调试内存分配器设置为在特定分配处中断。

http://msdn.microsoft.com/en-us/library/4wth1ha5.aspx

例如,将_CrtSetBreakAlloc(1378)添加到您的代码中会在分配编号为1378的块时触发调试断点。然后,您可以使用调试器跟踪代码返回到调用者。

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