New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory leak on executables embedded with 3.12 and later #113055
Comments
That doesn't look so nice, I can investigate. How are you measuring memory usage and what's the OS? In the previous versions of Python the obmalloc radix tree data structures were in the BSS and so most of RAM used by them was actually virtual and not real. I'm not sure if the move to interpreter state structures lost that behaviour but it could be cause for an increase in (real) memory use. Maybe there is also a memory leak there, e.g. malloc without free. |
Only the Task Manager on Windows 10 and 11 for me. |
I spent some time looking into this today. I suspect the fix is not going to be simple, unfortunately. What was previously global variables in the obmalloc.c module has become part of the PyInterpreterState structure. When an interpreter is freed, memory allocated by obmalloc doesn't get freed. I think that's the leak you are seeing. Previous to A possible way to fix this: when If you free the arenas and there is allocated blocks inside of them, you can't call |
Building gh-113218 with the address sanitizer and then running |
I can also personally confirm that on Windows, including the 32-bit version of Python. |
I think I've come up with a better fix. See gh-113412. |
ea2c001 has a memory leak to be fixed, which is found at gh-113412. |
Your
Trace log: https://gist.github.com/nascheme/fac2dd566f09fe3d75c98134f18c6170 E.g.
And
|
The following change seems to greatly reduce the leaks. The
|
My memory leak seems to come from cpython/Objects/unicodeobject.c Lines 14952 to 14973 in 0c57454
|
For the record, my memory gain reduced from 2M bytes to 200K bytes per cycle with gh-113412 (non-debug). Regarding the crash after re-initialization due to the full cleanup by
A dangling pointer in a non-isolable (small) extension could be worked around by making the interned string statically allocated, if any. Original issue: #100911 |
My previous comment about disabling the
If Py_DEBUG is enabled, then that function will free interned strings with the flag |
Yes, I believe a good portion of the memory leaks are coming from not cleaning up interned strings. This was intentional given that during the time that gh-19474 was merged, there were PyObjects leaks that would be referenced in the next Py_Initialize. Therefore, we couldn't safely clean up these interned strings. Given that these PyObjects leaks have now been largely fixed, it should be safe to re-enable this code: gh-113601. |
Context: (expand)runtime state and lifecyle
embedders:
interpreter lifecycle:
consolidating global runtime state
interpreter isolation
immortal objects (PEP 683):
extension modulesglobal state and interpreter isolation:
single-phase init ("legacy" extensions):
multi-phase init:
challenges:
obmalloc state
With all that in mind, here's where I understand we're at now:
From there, something along the lines of what @nascheme has proposed seems reasonable. We also need to clean up interned strings as @eduardo-elizondo has proposed. It would be nice if we could be a bit more proactive about cleaning up single-phase init modules (at the least static types defined therein), but that might not be feasible. One other option: use a process-global allocator for immortal, immutable objects (e.g. |
Regarding interned strings cleanup, see also: |
For interpreters that share state with the main interpreter, this points to the same static memory structure. For interpreters with their own obmalloc state, it is heap allocated. Add free_obmalloc_arenas() which will free the obmalloc arenas and radix tree structures for interpreters with their own obmalloc state. Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
) For interpreters that share state with the main interpreter, this points to the same static memory structure. For interpreters with their own obmalloc state, it is heap allocated. Add free_obmalloc_arenas() which will free the obmalloc arenas and radix tree structures for interpreters with their own obmalloc state. Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
Bug report
Bug description:
Since 67807cf,
_testembed.exe
(x64 Release) increases the memory usage in the cycle ofPy_Initialize
andPy_Finalize
.I monitored the Task Manager (
taskmgr.exe
) on Windows10/11, with a change againstPrograms/_testembed.c
:Commands:
_testembed.exe test_repeated_simple_init
orpython -m test test_embed -m test_simple_initialization_api
(no log)With RADIX TREE (as-is)
No RADIX TREE (as-is)
Recent
obmalloc.c
looks ready for finalization. Just adding my rough (invalid?) experiment made the leaks even. So, I hope that they share the same issue. Otherwise, ea2c001 or 15d4c9f has another leak. #98359 (comment)patch
With RADIX TREE (patched)
No RADIX TREE (patched)
cc @ericsnowcurrently @nascheme
CPython versions tested on:
3.12, 3.13, CPython main branch: 3aea6c4
Operating systems tested on:
Windows
Linked PRs
The text was updated successfully, but these errors were encountered: