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
(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:
importgcimportsysimportclrdefmain():
sys.path.append("./dlls") # the dll from the above class library is stored hereclr.AddReference("MemoryLeak")
importSystemfromMemoryLeakimportMemoryLeakdefevent_handler():
passfor_inrange(200):
example=MemoryLeak()
example.LeakEvent+=event_handlerexample.LeakEvent-=event_handlerexample.Dispose()
delexamplegc.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:
I 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.
changed the title [-]Memory link when attaching event handlers from Python[/-][+]Memory leak when attaching event handlers from Python[/+]on 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:
importSystem# run some code...System.GC.Collect()
...in my python script. I have tried this out and edited my original python script and the results are still the same.
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 MemoryLeak constructor class which exacerbates the memory leak as well. I will paste in the updated scripts and a summary of the results:
importgcimportsysimportclrdefmain():
sys.path.append("./dlls") # the dll from the above class library is stored hereclr.AddReference("MemoryLeak")
importSystemfromMemoryLeakimportMemoryLeakdefevent_handler():
passfor_inrange(200):
example=MemoryLeak()
example.LeakEvent+=event_handlerexample.LeakEvent-=event_handlerexample.Dispose()
delexamplegc.collect()
System.GC.Collect()
if__name__=="__main__":
main()
When run normally, we see the min and max of the GC.GetTotalMemory method 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.GetTotalMemory teeters 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!
Activity
sean-marten commented on 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.
lostmsu commented on Oct 13, 2022
I don't see you calling .NET GC anywhere.
sean-marten commented on Oct 13, 2022
Do you mean I am not manually calling collect?
sean-marten commented on 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 commented on 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 commented on 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 MemoryLeak constructor 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.GetTotalMemory method 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.GetTotalMemory teeters 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…