Repository navigation
Description
Environment
- Pythonnet version: 3.0.0
- Python version: 3.7.4
- Operating System: Windows 10
- .NET Framework 4.8.4515.0
Details
- There appears to be a memory leak when attaching a method to an event handler from Python. I was able to break this down into a very simple example. Below is my C# Library
using System;
namespace MemoryLeak
{
public Class MemoryLeak : IDisposable
{
public event EventHandler LeakEvent;
public MemoryLeak()
{
GC.Collect();
Console.WriteLine(GC.GetTotalMemory(true));
}
public void Dispose()
{
this.LeakEvent = null;
}
}
}(This was the file in my C# class library, just built and took the DLL to use in the below python script)
and the python script that utilizes this library:
import gc
import sys
import clr
def main():
sys.path.append("./dlls") # the dll from the above class library is stored here
clr.AddReference("MemoryLeak")
import System
from MemoryLeak import MemoryLeak
def event_handler():
pass
for _ in range(200):
example = MemoryLeak()
example.LeakEvent += event_handler
example.LeakEvent -= event_handler
example.Dispose()
del example
gc.collect()
System.GC.Collect()
if __name__ == "__main__":
main()If you run this python script successfully, you will notice that the total memory steadily increases. If you simply remove the following two lines from the python script regarding the addition of the event handler into the C# object, you will notice no memory increase:
example.LeakEvent += event_handler
example.LeakEvent -= event_handlerI would expect that between the cleanup of the attached handler, disposal and manual collections from both python and C#, that the event handler + memory leak class would get garbage collected properly, but they do not appear to be removed entirely based on memory tracking.
To note, I did attempt to make a C# console application that performs the same steps as the python script and the memory did not leak in that example.
I am more than happy to help contribute if this is deemed to be an issue with pythonnet, but would need a little guidance on where to start.
Activity
sean-marten commentedon Oct 13, 2022
Output from full python script (in bytes):
Output when event handler lines are removed (in bytes):
If you run even more cycles, the problem will exacerbate as well.
[-]Memory link when attaching event handlers from Python[/-][+]Memory leak when attaching event handlers from Python[/+]lostmsu commentedon Oct 13, 2022
I don't see you calling .NET GC anywhere.
sean-marten commentedon Oct 13, 2022
Do you mean I am not manually calling collect?
sean-marten commentedon Oct 13, 2022
@lostmsu Sorry I was typing over from a different computer, I was actually manually calling
GC.Collect();(and results are the same). If that is not what you meant, could you please clarify?EDIT: I saw a different issue where you also mentioned someone was not calling .NET GC, and by those comments I assume that you mean that I am not doing some sort of:
...in my python script. I have tried this out and edited my original python script and the results are still the same.
lostmsu commentedon Oct 13, 2022
@sean-marten please, doublecheck the output with all the GC calls, then if it reproduces, I can take a look at it.
sean-marten commentedon Oct 13, 2022
Okay @lostmsu , so I re-ran with the .NET GC collection calls both at the end of the python loop and in the dispose method of the C# library. Also for posterity I still am running the python gc collection at the end of the python loop. I also added a dummy array object to the
MemoryLeakconstructor class which exacerbates the memory leak as well. I will paste in the updated scripts and a summary of the results:When run normally, we see the min and max of the
GC.GetTotalMemorymethod return ~0.6 MB and ~8.6 MB, respectively. If you increase the number of loops in the python script from 200 to let's say 500, that becomes a min and max of ~0.6 MB and ~20.6 MB, respectively. And increasing the loop number only makes it worse, as expected.If you remove the python lines regarding registering the event handler from python, the output of
GC.GetTotalMemoryteeters between ~0.6 MB and ~2.1 MB, but never exceeds the larger value.Please let me know if you would like me to do any further diagnosis!
delete target object from event handler collections when it has no mo…
delete target object from event handler collections when it has no mo…
delete target object from event handler collections when it has no mo…