【问题标题】:valgrind and std::unique_ptr --- false positive or not?valgrind 和 std::unique_ptr --- 误报与否?
【发布时间】:2015-10-04 12:09:52
【问题描述】:

我正在尝试了解现代 C++ 中智能指针的使用,并且我编写了一个小而简单的程序来测试 valgrind。问题是下面的例子:

#include <iostream>
#include <memory>

class Base {
    private:
        virtual double meth_1( double x ) const = 0;
        virtual void meth_2( int y ) const = 0;
    protected:
        Base()
        {
            std::cout << "ctor of base for: " << this << std::endl;
        }
    public:
        virtual ~Base()
        {
            std::cout << "dtor of base for: " << this << std::endl;
        }
        double IMeth_1( double x ) const
        {
            return meth_1(x);
        }
        void IMeth_2( int y ) const
        {
            meth_2(y);
        }
};

class Derived_1 : public Base {
    private:
        double meth_1( double x ) const final
        {
            return x + 5.0;
        }
        void meth_2( int y ) const final
        {
            std::cout << (y + 5) << std::endl;
        }
    public:
        Derived_1() : Base()
        {
            std::cout << "ctor of Derived_1: " << this << std::endl;
        }
        ~Derived_1()
        {
            std::cout << "dtor of Derived_1: " << this << std::endl;
        }
};

class Derived_2 : public Base {
    private:
        double meth_1( double x ) const final
        {
            return x + 10.0;
        }
        void meth_2( int y ) const final
        {
            std::cout << (y + 10) << std::endl;
        }
    public:
        Derived_2() : Base()
        {
            std::cout << "ctor of Derived_2: " << this << std::endl;
        }
        ~Derived_2()
        {
            std::cout << "dtor of Derived_2: " << this << std::endl;
        }
};

void Fun( const Base& crBase )
{
    crBase.IMeth_2( 5 );
}

int main( int argc, char* argv[] ) {
    std::unique_ptr< Base > upBase;

    for ( std::size_t idx = 0ul; idx < 2ul; idx++ ) {
        upBase      = std::make_unique< Derived_1 >();
        std::cout   << upBase->IMeth_1( idx )   << std::endl;
        upBase->IMeth_2( idx );
        std::cout   << "----------"             << std::endl;
    }

    for ( std::size_t idx = 0ul; idx < 2ul; idx++ ) {
        upBase      = std::make_unique< Derived_2 >();
        std::cout   << upBase->IMeth_1( idx )   << std::endl;
        upBase->IMeth_2( idx );
        std::cout   << "----------"             << std::endl;
    }

    upBase = std::make_unique< Derived_1 >();
    Fun( *upBase );

    return 0;
}

使用valgrind --leak-check=full --show-leak-kinds=all &lt;prog_name&gt; 运行时会出现内存泄漏:

==32350== HEAP SUMMARY:
==32350==     in use at exit: 72,704 bytes in 1 blocks
==32350==   total heap usage: 6 allocs, 5 frees, 72,744 bytes allocated
==32350== 
==32350== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==32350==    at 0x4C28C10: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==32350==    by 0x4EBE1EF: pool (eh_alloc.cc:117)
==32350==    by 0x4EBE1EF: __static_initialization_and_destruction_0 (eh_alloc.cc:244)
==32350==    by 0x4EBE1EF: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:307)
==32350==    by 0x400F279: call_init.part.0 (in /usr/lib/ld-2.22.so)
==32350==    by 0x400F38A: _dl_init (in /usr/lib/ld-2.22.so)
==32350==    by 0x4000DB9: ??? (in /usr/lib/ld-2.22.so)
==32350== 
==32350== LEAK SUMMARY:
==32350==    definitely lost: 0 bytes in 0 blocks
==32350==    indirectly lost: 0 bytes in 0 blocks
==32350==      possibly lost: 0 bytes in 0 blocks
==32350==    still reachable: 72,704 bytes in 1 blocks
==32350==         suppressed: 0 bytes in 0 blocks
==32350== 
==32350== For counts of detected and suppressed errors, rerun with: -v
==32350== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

72,704 字节的块在退出时是否仍在使用中是误报,还是我误用了智能指针?我假设我没有进行任何类型的切片,因为每次删除对象时都会调用基本 dtor。

对不起,如果这是一个愚蠢的问题,但我在 SO 中找不到任何与 valgrind/false positive/unique_ptr 相关的主题。此外,我不知道可能在unique_ptr 中创建了类似于shared_ptr 中的任何其他块来跟踪对象。

编辑: 不是 Still Reachable Leak detected by Valgrind 的重复项,因为在我的情况下我没有使用线程(众所周知,valgrind 在尤其是 OpenMPI 环境中会给出误报)。此外,在另一个问题中,问题是通过对提供的代码进行适当修改来解决的。尽管如此,对于什么叫真正的内存泄漏,没有任何共识——也就是说,在退出时仍在使用的可达块是否应该 /em> 是否被视为内存泄漏

【问题讨论】:

  • 看起来像一个 valgrind 错误,它正在检测自己的分配...您对 std::unique_ptr 的使用看起来不错。
  • 看一眼代码,它看起来不错,但您应该尝试减少它,因为这里似乎仍然有很多不必要的代码来重现问题。作为在 valgrind 中测试小东西的一般规则,将整个测试放入一个函数并多次调用它。然后,如果丢失块的数量增加,你就知道你有真正的内存泄漏。
  • 感谢你们两位的cmets。我将尝试这种函数方法来查看退出时使用的块是增加还是保持不变,@Dave。
  • 您确定这是内存泄漏吗? Valgrind 似乎抱怨可引用(可访问,非泄漏)分配的数据,这些数据可能由系统库分配一次,并打算在整个应用程序生命周期中保留。

标签: c++ valgrind c++14 smart-pointers


【解决方案1】:

这不是 valgrind 错误。这是一个 libstdc++ 特定的功能,即 介绍于http://gcc.gnu.org/viewcvs/gcc?view=revision&revision=219988

如果您查看代码,您会发现来自 libstdc++-v3/libsupc++/eh_alloc.cc 的类池没有析构函数,因为它是一个紧急内存池,旨在在整个运行时保留应用。

即使是最小的程序也会显示问题:

 ~ % echo "int main () {}" | g++ -x c++ -
 ~ % valgrind --leak-check=full --show-leak-kinds=all ./a.out
==502== Memcheck, a memory error detector
==502== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==502== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==502== Command: ./a.out
==502== 
==502== 
==502== HEAP SUMMARY:
==502==     in use at exit: 72,704 bytes in 1 blocks
==502==   total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==502== 
==502== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==502==    at 0x402CC6F: malloc (vg_replace_malloc.c:299)
==502==    by 0x40F420F: _GLOBAL__sub_I_eh_alloc.cc (in /usr/lib64/gcc/x86_64-pc-linux-gnu/5.2.1/libstdc++.so.6.0.21)
==502==    by 0x4010AA4: call_init.part.0 (dl-init.c:72)
==502==    by 0x4010D44: call_init (dl-init.c:30)
==502==    by 0x4010D44: _dl_init (dl-init.c:120)
==502==    by 0x4000C79: ??? (in /lib64/ld-2.22.90.so)

【讨论】:

  • 如果这是真的,我认为这仍然会成为一个 valgrind 错误。 valgrind 应该有一个抑制列表,这样就不会警告众所周知的误报——来自标准库的分配,这些分配并不真正表明正在测试的应用程序中存在任何内存泄漏。根据您的回答,黑名单似乎缺少一个条目。
  • 明天我会问那个代码的作者,如果缺少的析构函数真的是有意的。
  • It's already been pointed out in the PR leading to the change you linked to. 简而言之:这很棘手,需要考虑一些极端情况。
  • 已经有一个针对此问题的开放 valgrind 错误:bugs.kde.org/show_bug.cgi?id=345307
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-29
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 2012-11-26
  • 2011-07-01
  • 2022-01-20
相关资源
最近更新 更多