throw() with clang++ 显示可能的内存泄漏
throw() with clang++ shows possible memory leak
我有一段代码,用 g++ 编译时没有显示任何内存泄漏。
然而,使用 clang++ 编译时同样显示,可能存在内存泄漏。
这是踪迹,
==7115==
==7115== HEAP SUMMARY:
==7115== in use at exit: 16 bytes in 1 blocks
==7115== total heap usage: 2,324 allocs, 2,323 frees, 2,166,060 bytes allocated
==7115==
==7115== 16 bytes in 1 blocks are still reachable in loss record 1 of 1
==7115== at 0x4C2BFB9: calloc (vg_replace_malloc.c:762)
==7115== by 0x4129830: __cxxabiv1::__calloc_with_fallback(unsigned long, unsigned long) (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x4128946: __cxa_get_globals (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x412B287: __cxa_throw (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x4E712AE: Lib::GenCmd::RaiseException(Status, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) (LibBase.cpp:291)
==7115==
==7115== LEAK SUMMARY:
==7115== definitely lost: 0 bytes in 0 blocks
==7115== indirectly lost: 0 bytes in 0 blocks
==7115== possibly lost: 0 bytes in 0 blocks
==7115== still reachable: 16 bytes in 1 blocks
==7115== suppressed: 0 bytes in 0 blocks
==7115==
==7115== For counts of detected and suppressed errors, rerun with: -v
好吧,无法共享代码片段,但我可以告诉你 RaiseException() 是我调用 throw() 的函数(在第291) 一个例外。这是函数片段:
void GenCmd::RaiseException(Status status, std::string AdditionalMsg) throw(Status) {
s_last_error = GetStatusString(status);
if (false == AdditionalMsg.empty()) {
s_last_error = s_last_error + AdditionalMsg;
}
throw(status);
}
Status 是一个结构,定义如下(以及默认、参数化和复制构造函数)
typedef struct _Status {
const u64_t m_status : 8;
const u64_t ReservedByte1 : 8;
const u64_t m_action : 8;
const u64_t ReservedByte3 : 5;
const u64_t m_testbit1 : 1;
const u64_t m_testbit2 : 1;
const u64_t m_cmd_failure : 1;
const u64_t m_module_code : 4;
const u64_t m_file_code : 8;
const u64_t ReservedByte7 : 4;
const u64_t m_line_no : 16;
}Status
事实是,GCC 没有发现泄漏,但只有 Clang 出现,这让我认为这是 Clang 的一些问题。 (对于 Clang,我的意思是它也可以是 libcxxabi)
我正在浏览 clang 的源代码,& __cxa_get_globals() 是 calloc() 的函数打电话。我还不确定 clang 的执行流程。
任何想法或任何输入都可以确认这是一个 Clang 问题而不是我的代码问题?
这是我使用的 clang 版本。代码是用 C++11 编译的,另外还有 '-stdlib=libc++, '-lc++', '-lc++abi'。
[user~]$ clang --version
clang version 7.1.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
- 更新:此异常是从构造函数中引发的。
更新
我写了另一个虚拟代码来查看 Clang 的行为,& 看来问题实际上与 Clang (libc++abi) 有关。
看看下面的原始代码
#include <iostream>
#include <stdexcept>
class Positive {
int m_number1;
public:
Positive() : m_number1(10) {
}
Positive(int no) {
if (no < 0) {
// throw 100;
throw std::invalid_argument("Send positive nu");
} else {
m_number1 = no;
}
}
~Positive() {
}
void print() {
std::cout<< "Value of member is: " <<m_number1 <<std::endl;
}
};
int main() {
try {
Positive p1;
p1.print();
Positive p2(100);
p2.print();
Positive p3(-10);
p3.print();
} catch(...) {
std::cout << "Some Exception occured" <<std::endl;
}
return 0;
}
即使在执行上面的代码时,我在 Valgrind 上也看到了相同的结果。这是输出:
[user]$ valgrind --leak-check=full --leak-resolution=high --show-leak-kinds=all ./a.out
==119789== Memcheck, a memory error detector
==119789== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==119789== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==119789== Command: ./a.out
==119789==
Value of member is: 10
Value of member is: 100
Some Exception occured
==119789==
==119789== HEAP SUMMARY:
==119789== in use at exit: 16 bytes in 1 blocks
==119789== total heap usage: 3 allocs, 2 frees, 201 bytes allocated
==119789==
==119789== 16 bytes in 1 blocks are still reachable in loss record 1 of 1
==119789== at 0x4C2BFB9: calloc (vg_replace_malloc.c:762)
==119789== by 0x40FF830: __cxxabiv1::__calloc_with_fallback(unsigned long, unsigned long) (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x40FE946: __cxa_get_globals (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x4101287: __cxa_throw (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x4014B0: Positive::Positive(int) (in /home/user/test/a.out)
==119789== by 0x4010F9: main (in /home/user/test/a.out)
==119789==
==119789== LEAK SUMMARY:
==119789== definitely lost: 0 bytes in 0 blocks
==119789== indirectly lost: 0 bytes in 0 blocks
==119789== possibly lost: 0 bytes in 0 blocks
==119789== still reachable: 16 bytes in 1 blocks
==119789== suppressed: 0 bytes in 0 blocks
==119789==
==119789== For counts of detected and suppressed errors, rerun with: -v
==119789== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
[user]$
有趣的是,它显示了 3 次分配。我假设它与第三个对象相关,但我如何确保它被清除(或未自行分配)?
可能,同样的事情可以帮助我修复我的原始代码。
我也发现了这个问题。如果您的代码至少抛出一次任何东西,它实际上会发生,所以最简单的重现场景就是 运行 这段代码:
int
main()
{
try {
throw 42;
} catch (int) {}
}
但是,如果您多次抛出,仍然只有一个 16 字节的“泄漏”块(这并不是真正的泄漏,因为它仍然可以从某个全局变量访问)。深入研究 libc++abi 库 sources 表明,此类块实际上为每个线程分配一次以保存一些异常处理上下文,并且当线程被销毁时每个块都被释放,因为它使用 TLS 并注册了适当的析构函数。所以毕竟,它看起来完全安全,不是问题。
我有一段代码,用 g++ 编译时没有显示任何内存泄漏。 然而,使用 clang++ 编译时同样显示,可能存在内存泄漏。
这是踪迹,
==7115==
==7115== HEAP SUMMARY:
==7115== in use at exit: 16 bytes in 1 blocks
==7115== total heap usage: 2,324 allocs, 2,323 frees, 2,166,060 bytes allocated
==7115==
==7115== 16 bytes in 1 blocks are still reachable in loss record 1 of 1
==7115== at 0x4C2BFB9: calloc (vg_replace_malloc.c:762)
==7115== by 0x4129830: __cxxabiv1::__calloc_with_fallback(unsigned long, unsigned long) (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x4128946: __cxa_get_globals (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x412B287: __cxa_throw (in /opt/xxx/lib64/libc++abi.so.1)
==7115== by 0x4E712AE: Lib::GenCmd::RaiseException(Status, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) (LibBase.cpp:291)
==7115==
==7115== LEAK SUMMARY:
==7115== definitely lost: 0 bytes in 0 blocks
==7115== indirectly lost: 0 bytes in 0 blocks
==7115== possibly lost: 0 bytes in 0 blocks
==7115== still reachable: 16 bytes in 1 blocks
==7115== suppressed: 0 bytes in 0 blocks
==7115==
==7115== For counts of detected and suppressed errors, rerun with: -v
好吧,无法共享代码片段,但我可以告诉你 RaiseException() 是我调用 throw() 的函数(在第291) 一个例外。这是函数片段:
void GenCmd::RaiseException(Status status, std::string AdditionalMsg) throw(Status) {
s_last_error = GetStatusString(status);
if (false == AdditionalMsg.empty()) {
s_last_error = s_last_error + AdditionalMsg;
}
throw(status);
}
Status 是一个结构,定义如下(以及默认、参数化和复制构造函数)
typedef struct _Status {
const u64_t m_status : 8;
const u64_t ReservedByte1 : 8;
const u64_t m_action : 8;
const u64_t ReservedByte3 : 5;
const u64_t m_testbit1 : 1;
const u64_t m_testbit2 : 1;
const u64_t m_cmd_failure : 1;
const u64_t m_module_code : 4;
const u64_t m_file_code : 8;
const u64_t ReservedByte7 : 4;
const u64_t m_line_no : 16;
}Status
事实是,GCC 没有发现泄漏,但只有 Clang 出现,这让我认为这是 Clang 的一些问题。 (对于 Clang,我的意思是它也可以是 libcxxabi)
我正在浏览 clang 的源代码,& __cxa_get_globals() 是 calloc() 的函数打电话。我还不确定 clang 的执行流程。
任何想法或任何输入都可以确认这是一个 Clang 问题而不是我的代码问题?
这是我使用的 clang 版本。代码是用 C++11 编译的,另外还有 '-stdlib=libc++, '-lc++', '-lc++abi'。
[user~]$ clang --version
clang version 7.1.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
- 更新:此异常是从构造函数中引发的。
更新 我写了另一个虚拟代码来查看 Clang 的行为,& 看来问题实际上与 Clang (libc++abi) 有关。
看看下面的原始代码
#include <iostream>
#include <stdexcept>
class Positive {
int m_number1;
public:
Positive() : m_number1(10) {
}
Positive(int no) {
if (no < 0) {
// throw 100;
throw std::invalid_argument("Send positive nu");
} else {
m_number1 = no;
}
}
~Positive() {
}
void print() {
std::cout<< "Value of member is: " <<m_number1 <<std::endl;
}
};
int main() {
try {
Positive p1;
p1.print();
Positive p2(100);
p2.print();
Positive p3(-10);
p3.print();
} catch(...) {
std::cout << "Some Exception occured" <<std::endl;
}
return 0;
}
即使在执行上面的代码时,我在 Valgrind 上也看到了相同的结果。这是输出:
[user]$ valgrind --leak-check=full --leak-resolution=high --show-leak-kinds=all ./a.out
==119789== Memcheck, a memory error detector
==119789== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==119789== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==119789== Command: ./a.out
==119789==
Value of member is: 10
Value of member is: 100
Some Exception occured
==119789==
==119789== HEAP SUMMARY:
==119789== in use at exit: 16 bytes in 1 blocks
==119789== total heap usage: 3 allocs, 2 frees, 201 bytes allocated
==119789==
==119789== 16 bytes in 1 blocks are still reachable in loss record 1 of 1
==119789== at 0x4C2BFB9: calloc (vg_replace_malloc.c:762)
==119789== by 0x40FF830: __cxxabiv1::__calloc_with_fallback(unsigned long, unsigned long) (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x40FE946: __cxa_get_globals (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x4101287: __cxa_throw (in /usr/local/lib/libc++abi.so.1.0)
==119789== by 0x4014B0: Positive::Positive(int) (in /home/user/test/a.out)
==119789== by 0x4010F9: main (in /home/user/test/a.out)
==119789==
==119789== LEAK SUMMARY:
==119789== definitely lost: 0 bytes in 0 blocks
==119789== indirectly lost: 0 bytes in 0 blocks
==119789== possibly lost: 0 bytes in 0 blocks
==119789== still reachable: 16 bytes in 1 blocks
==119789== suppressed: 0 bytes in 0 blocks
==119789==
==119789== For counts of detected and suppressed errors, rerun with: -v
==119789== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
[user]$
有趣的是,它显示了 3 次分配。我假设它与第三个对象相关,但我如何确保它被清除(或未自行分配)?
可能,同样的事情可以帮助我修复我的原始代码。
我也发现了这个问题。如果您的代码至少抛出一次任何东西,它实际上会发生,所以最简单的重现场景就是 运行 这段代码:
int
main()
{
try {
throw 42;
} catch (int) {}
}
但是,如果您多次抛出,仍然只有一个 16 字节的“泄漏”块(这并不是真正的泄漏,因为它仍然可以从某个全局变量访问)。深入研究 libc++abi 库 sources 表明,此类块实际上为每个线程分配一次以保存一些异常处理上下文,并且当线程被销毁时每个块都被释放,因为它使用 TLS 并注册了适当的析构函数。所以毕竟,它看起来完全安全,不是问题。