【问题标题】:std::string operator+() memory leak?std::string operator+() 内存泄漏?
【发布时间】:2011-11-09 04:56:02
【问题描述】:

我很担心,因为我写了一个小应用程序,如果我相信 valgrind 似乎有内存泄漏(我实际上做了什么):

==9321== 251 bytes in 7 blocks are definitely lost in loss record 1 of 1
==9321==    at 0x402569A: operator new(unsigned int) (vg_replace_malloc.c:255)
==9321==    by 0x40D3D05: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.13)
==9321==    by 0x40D4977: std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.13)
==9321==    by 0x40D57AC: std::string::reserve(unsigned int) (in /usr/lib/libstdc++.so.6.0.13)
==9321==    by 0x40D5EE6: std::string::operator+=(char) (in /usr/lib/libstdc++.so.6.0.13)
==9321==    by 0x804E113: xl2::TextParser::getNextLfLine() (TextParser.cpp:162)
==9321==    by 0x804BFD5: xl2::UsbTree::parseStringInfo(xl2::TextParser&, std::string&, std::string&) (UsbTree.cpp:362)
==9321==    by 0x804B881: xl2::UsbTree::parseDevicesFile(std::string) (UsbTree.cpp:204)
==9321==    by 0x804B34E: xl2::UsbTree::updateTree() (UsbTree.cpp:70)
==9321==    by 0x804E2E4: scan(std::string) (testUsbTree.cpp:75)
==9321==    by 0x804E6CC: executeCommand(std::string) (testUsbTree.cpp:132)
==9321==    by 0x804E8F6: hushLoop() (testUsbTree.cpp:153)

这是有问题的功能:

/**
 * Returns the next line separated by UNIX style LF
 * @return The next line separated by UNIX style LF
 */
std::string TextParser::getNextLfLine()
{
    std::string line;   // The builded line

    while(this->hasMoreToken())
    {
        line += this->m_pText[this->m_iTokenLocation++];

        // Check if we have just seen a CR/LF character
        if(this->m_pText[this->m_iTokenLocation - 1] == '\n')
            return line;
    }

    return line;
}

程序通过离开主函数正确终止(不调用 exit())。

我只是不明白为什么会出现内存泄漏。由于我的字符串被复制到堆栈中,并且应该在离开函数时清除原始字符串,对吗?或者错误可能更高?在顶层,我还将返回的值分配给一个局部变量,然后将其作为字段放入对象中(通过复制)...

所以我想知道泄漏是来自标准库还是来自 valgrind,这真的很令人惊讶!

非常感谢任何指向未泄漏内存的指针:-p!

【问题讨论】:

  • 实际上我有一个内存泄漏,因为我忘记声明一个虚拟基类的析构函数,然后运行 ​​valgrind 向我展示了一个完美的内存无泄漏应用程序!道德:除非你真的知道自己在做什么,否则无论如何都要担心 STL 内存泄漏!

标签: c++ string stl memory-leaks valgrind


【解决方案1】:

引用Valgrind FAQ

对于 GCC 2.91、2.95、3.0 和 3.1,使用带有 -D__USE_MALLOC 的 STL 编译所有源代码。谨防!从 3.3 版开始,它已从 GCC 中删除。

对于 GCC 3.2.2 及更高版本,您应该在运行程序之前导出环境变量 GLIBCPP_FORCE_NEW。

在 GCC 3.4 及更高版本中,该变量已更改名称为 GLIBCXX_FORCE_NEW。

也在GCC FAQ中讨论过

【讨论】:

  • 这个答案真的解决了问题吗?这会让我感到惊讶。我想,自从 GCC 4.1 以来,我就不必做这些技巧了。
【解决方案2】:

我不会太担心 STL 泄漏。

默认的 STL 分配器(用于 gcc)使用巧妙的技巧来最大化 Valgrind 经常报告为泄漏的效率。值得注意的是,它会池化内存,这意味着当您清除 string 时它会保留内存,并且可能会在您下次插入 mapvector 时重用它。

我不认为池本身在程序结束时被正确处置,可能是为了避免静态破坏命令惨败(即,假设池被处置,然后您请求内存,urk)。因此它会泄漏......就在您的程序结束并且操作系统强制取回内存之前。

【讨论】:

  • 当然,没有合理的析构函数会分配内存(或做任何其他可能抛出的事情),所以释放它应该可以,但显然他们已经决定容忍这种虐待。
  • @spraff:你会觉得在关机期间登录很奇怪吗?
  • 分配内存不一定能抛出,可以用nothrow new。或者只是在它离开析构函数之前捕获异常。无论如何,当不合理的事情根据标准定义了行为时,不允许标准库实现仅仅因为您做了不合理的事情而崩溃。第三方库可以说,“如果你的全局析构函数分配内存,事情就会顺利进行”,但标准库不能。
  • @Matthieu 可以在不分配内存的情况下写入日志
  • @Steve 确信我们可以使用非抛出 malloc,然后它就变得合理了。此外,标准 语言 并不完全 支持抛出析构函数(在异常期间),因此库不必支持它们或以其他方式声明。
【解决方案3】:

我怀疑编译器正在使用 NRVO 将您的临时字符串放入其实际返回位置。然后将该返回字符串存储在一个已分配并从堆中泄漏的对象中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 2014-08-18
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多