那么让我们说我分配一些内存的C / C ++代码,并返回一个指向它的指针。
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
void Allocate(void **p) {
int N=2048;
*p=malloc(N);
}
#ifdef __cplusplus
}
#endif
显然,我希望释放这块内存是我的责任。 现在假设我将它编译成一个共享库并使用ctypes从Python调用它,但是没有显式释放该内存。
import ctypes
from ctypes import cdll, Structure, byref
external_lib = cdll.LoadLibrary('libtest.so.1.0')
ptr=ctypes.c_void_p(0)
external_lib.Allocate(ctypes.byref(ptr))
如果我用valgrind运行这个脚本,如果我编译没有'-O3'标志的test.cpp,我会得到2048字节的内存泄漏。但是如果我使用'-O3'标志编译它,那么我不会得到内存泄漏。
这不是一个真正的问题 - 我会小心地明确释放我分配的任何内存。但我很好奇这种行为来自哪里。
我使用linux中的以下脚本对此进行了测试。
g++ -Wall -c -fPIC -fno-common test.cpp -o libtest1.o
g++ -shared -Wl,-soname,libtest1.so.1 -o libtest1.so.1.0 libtest1.o
g++ -O3 -Wall -c -fPIC -fno-common test.cpp -o libtest2.o
g++ -shared -Wl,-soname,libtest2.so.1 -o libtest2.so.1.0 libtest2.o
valgrind python test1.py &> report1
valgrind python test2.py &> report2
使用以下输出
报告1:
==27875== LEAK SUMMARY:
==27875== definitely lost: 2,048 bytes in 1 blocks
==27875== indirectly lost: 0 bytes in 0 blocks
==27875== possibly lost: 295,735 bytes in 1,194 blocks
==27875== still reachable: 744,633 bytes in 5,025 blocks
==27875== suppressed: 0 bytes in 0 blocks
报告2:
==27878== LEAK SUMMARY:
==27878== definitely lost: 0 bytes in 0 blocks
==27878== indirectly lost: 0 bytes in 0 blocks
==27878== possibly lost: 295,735 bytes in 1,194 blocks
==27878== still reachable: 746,681 bytes in 5,026 blocks
==27878== suppressed: 0 bytes in 0 blocks
答案 0 :(得分:4)
根据平台的不同,不同的用户似乎会获得不同的结果。我试图用Python 2.5.5,Python 2.6.8,Python 3.2.3和g ++ 4.7.2在Debian Wheezy系统上重现这个问题。
基于你的代码,你知道它是漏洞,只是valgrind以不同方式报告内存使用情况。在报告1中,绝对没有提及2048块。在报告2中,它列在still reachable
部分。
valgrind leak detector documentation描述了如何检测泄漏。有趣的是,它会在内存和每个线程的通用寄存器集中查找引用。可以想象(但我认为不太可能)当泄漏检测器在程序退出时运行时,其中一个CPU寄存器中仍然存在已分配的存储器的引用。对于未经优化的版本,Allocate
函数中可能存在附加指令,这些指令会破坏可能包含泄漏引用的任何寄存器信息。在优化版本上,Allocate
函数可以在寄存器中保留引用,并将结果存储在*p
中。
当然,如果不能重现这一点,那就是猜测。您可以请求valgrind
输出有关其找到的引用的更多信息,这些信息可以提供有关已分配块的更多信息。
例如。这将显示可访问和无法访问的块。
valgrind --show-reachable=yes --leak-check=full python2.5 test1.py &> report1-2.5
如果我将您的代码修改为以下内容,我系统上的所有测试都表明2048块肯定会丢失(即使已经分配了4096个字节)。这也让我相信它可能是某种缓存的寄存器值,正由valgrind的检漏仪接收。
import ctypes
from ctypes import cdll, Structure, byref
external_lib = cdll.LoadLibrary('libtest.so.1.0')
ptr=ctypes.c_void_p(0)
external_lib.Allocate(ctypes.byref(ptr))
external_lib.Allocate(ctypes.byref(ptr)) # <-- Allocate a second block, the first becomes lost.
这是来自valgrind的结果片段,显示可到达和无法访问的块:
==28844== 2,048 bytes in 1 blocks are still reachable in loss record 305 of 366
==28844== at 0x4C28BED: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28844== by 0x6CD870F: Allocate (in /projects/stack-overflow/18929183-python-garbage-collector-behavior-with-ctypes/libtest1.so.1.0)
==28844== by 0x6ACEDEF: ffi_call_unix64 (in /usr/lib/python2.6/lib-dynload/_ctypes.so)
==28844== by 0x6ACE86A: ffi_call (in /usr/lib/python2.6/lib-dynload/_ctypes.so)
==28844== by 0x6AC9A66: _CallProc (callproc.c:816)
==28844== by 0x6AC136C: CFuncPtr_call (_ctypes.c:3860)
==28844== by 0x424989: PyObject_Call (abstract.c:2492)
==28844== by 0x4A17B8: PyEval_EvalFrameEx (ceval.c:3968)
==28844== by 0x49F0D1: PyEval_EvalCodeEx (ceval.c:3000)
==28844== by 0x49F211: PyEval_EvalCode (ceval.c:541)
==28844== by 0x4C66FE: PyRun_FileExFlags (pythonrun.c:1358)
==28844== by 0x4C7A36: PyRun_SimpleFileExFlags (pythonrun.c:948)
==28844==
==28844== 2,048 bytes in 1 blocks are definitely lost in loss record 306 of 366
==28844== at 0x4C28BED: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28844== by 0x6CD870F: Allocate (in /projects/stack-overflow/18929183-python-garbage-collector-behavior-with-ctypes/libtest1.so.1.0)
==28844== by 0x6ACEDEF: ffi_call_unix64 (in /usr/lib/python2.6/lib-dynload/_ctypes.so)
==28844== by 0x6ACE86A: ffi_call (in /usr/lib/python2.6/lib-dynload/_ctypes.so)
==28844== by 0x6AC9A66: _CallProc (callproc.c:816)
==28844== by 0x6AC136C: CFuncPtr_call (_ctypes.c:3860)
==28844== by 0x424989: PyObject_Call (abstract.c:2492)
==28844== by 0x4A17B8: PyEval_EvalFrameEx (ceval.c:3968)
==28844== by 0x49F0D1: PyEval_EvalCodeEx (ceval.c:3000)
==28844== by 0x49F211: PyEval_EvalCode (ceval.c:541)
==28844== by 0x4C66FE: PyRun_FileExFlags (pythonrun.c:1358)
==28844== by 0x4C7A36: PyRun_SimpleFileExFlags (pythonrun.c:948)
答案 1 :(得分:-1)
此行为来自gcc -O3优化。 gcc发现分配的内存未使用,省略了此代码块。
您可以参考以下问题:malloc and gcc optimization 2