…et#2719)
* Fix MethodBinding/OverloadMapper memory leak (pythonnet#691)
MethodBinding and OverloadMapper held PyObject `target` references that
were not disposed during tp_clear, leaving Python-side refcount drops to
wait on the multi-hop .NET finalizer chain. They also shared the same
C# PyObject instance across mp_subscript/Overloads paths, so freeing one
could free the underlying Python object out from under the others.
- ExtensionType: add virtual OnClear() hook called from tp_clear before
the GCHandle is released, letting subclasses eagerly drop owned
Python references.
- MethodBinding/OverloadMapper: override OnClear to dispose `target`.
(`targetType` is intentionally not disposed since Python types are
long-lived and tracked by other caches.)
- Take an independent INCREF'd PyObject copy at every site that hands a
shared target into a new MethodBinding or OverloadMapper, so each
wrapper owns its own reference.
Result: the three _does_not_leak_memory tests drop from ~485 MB delta
to ~10 KB delta on Python 3.14.
* Tighten leak-test threshold to 10% to actually fail the bug
The previous 90% threshold (0.9 MB/iter against a 1 MB allocation)
documented the issue but did not reproduce it: master leaks
~600-765 KB/iter, which the 0.9 MB threshold accepts as passing.
Drop the threshold to 10% (104 KB/iter). On the 2026-05-09 verification
run with Python 3.14 GIL on linux-aarch64:
Without fix (master): ~572-765 KB/iter (FAIL)
With fix (this branch): ~-500 B/iter (PASS)
Margin is roughly 6x in either direction across .NET 8 and .NET 10, so
the threshold cleanly separates buggy from fixed states without being
sensitive to GC noise.
* Bugfix and improvements
- Handle the `PyType` reference in `OverloadMapper` and `MethodBinding` in the
same way as the object reference
- Unconditionally store the `PyType` of the object
- Introduce `NewReference` helper function for the object and type
passing
- Fix the remaining missing reference count bump for the type
(`MethodObject`)
- As the count is now correct, `Dispose` the type as well
---------
Co-authored-by: Benedikt Reinartz <filmor@gmail.com>
(cherry picked from commit ca323cc)
Cherry-pick from upstream pythonnet/pythonnet: fixes a long-standing memory leak in MethodBinding/OverloadMapper where references to the target/target-type were not owned/disposed.
Conflict note: small conflicts in MethodObject.cs, OverloadMapper.cs, and test_method.py were resolved against this fork. The fork's ClassManager.GetClass(t) is => ReflectedClrType.GetOrCreate(t), so the upstream form (ReflectedClrType.GetOrCreate(...).NewReference()) is equivalent here; the leak fix adds owned references that are disposed in OnClear. Builds clean (Python.Runtime.csproj). Worth a careful review of the reference-ownership given the fork's divergence.
Relevant to long-running embedded use (e.g. Lean).
🤖 Generated with Claude Code