Ответ 1
Похоже, что разные пользователи получают разные результаты в зависимости от своей платформы. Я попытался воспроизвести эту проблему безуспешно в системе Debian Wheezy с Python 2.5.5, Python 2.6.8, Python 3.2.3 с g++ 4.7.2.
Основываясь на вашем коде, вы знаете, что он протекает, это просто, что valgrind сообщает об использовании памяти по-разному. В отчете 1 определенно нет ссылки на кусок 2048. В отчете 2 он указан в разделе still reachable
.
документация о дефектоскопии valgrind описывает, как обнаруживаются утечки. Интересно отметить, что он ищет ссылки как в памяти, так и в регистре общего назначения для каждого потока. Было бы возможно (но, я бы подумал, маловероятно), что, когда детектор течи запускается при выходе программы, по-прежнему сохраняется ссылка в одном из регистров процессора в выделенную память. Для неоптимизированной версии в функции 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)