valgrind 和 std::unique_ptr --- 误报与否?
valgrind and std::unique_ptr --- false positive or not?
我试图理解智能指针在现代 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 <prog_name>
:
时出现内存泄漏
==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-related 主题。此外,我不知道在 unique_ptr
中可能创建了与 shared_ptr
中类似的任何其他块来跟踪对象。
编辑: 不是 Still Reachable Leak detected by Valgrind 的副本,因为在我的情况下我没有使用线程(并且已知 valgrind
会在,尤其是 OpenMPI 环境)。而且,在另一个问题中,通过适当修改提供的代码解决了问题。尽管如此,对于什么叫做 真正的内存泄漏 --- 也就是说, 可访问块 应该是 在退出时仍在使用是否被视为内存泄漏。
这不是 valgrind 错误。这是一个 libstdc++ 特定的功能,即
在 http://gcc.gnu.org/viewcvs/gcc?view=revision&revision=219988.
中引入
如果您查看代码,您会发现来自 libstdc++-v3/libsupc++/eh_alloc.cc 的 class 池没有析构函数,因为它是一个紧急内存旨在在应用程序的整个运行期间保留的池。
即使是最小的程序也会显示问题:
~ % 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)
我试图理解智能指针在现代 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 <prog_name>
:
==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-related 主题。此外,我不知道在 unique_ptr
中可能创建了与 shared_ptr
中类似的任何其他块来跟踪对象。
编辑: 不是 Still Reachable Leak detected by Valgrind 的副本,因为在我的情况下我没有使用线程(并且已知 valgrind
会在,尤其是 OpenMPI 环境)。而且,在另一个问题中,通过适当修改提供的代码解决了问题。尽管如此,对于什么叫做 真正的内存泄漏 --- 也就是说, 可访问块 应该是 在退出时仍在使用是否被视为内存泄漏。
这不是 valgrind 错误。这是一个 libstdc++ 特定的功能,即 在 http://gcc.gnu.org/viewcvs/gcc?view=revision&revision=219988.
中引入如果您查看代码,您会发现来自 libstdc++-v3/libsupc++/eh_alloc.cc 的 class 池没有析构函数,因为它是一个紧急内存旨在在应用程序的整个运行期间保留的池。
即使是最小的程序也会显示问题:
~ % 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)