Python/WinRT memory leaks
Willy-JL opened this issue ยท 5 comments
I noticed that using windows.media.control
can often lead to memory leaks, seemingly uncontrollable from python's side of things.
Example demonstration:
from winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager
from pympler.tracker import SummaryTracker
import asyncio
import gc
async def get_media_info():
# Memory leaking calls
sessions = await MediaManager.request_async()
current_session = sessions.get_current_session()
await current_session.try_get_media_properties_async()
# Force garbage collect, does nothing
gc.collect()
return
if __name__ == '__main__':
# Pause to check starting memory usage (I used task manager)
# ~ 30 MB in my case
input()
# Track objects and sizes
tracker = SummaryTracker()
# 500 iterations is a good example amount
for _ in range(500):
# Leak memory
asyncio.run(get_media_info())
# Force garbage collect, does nothing
gc.collect()
# Print info on object count and size differences
# Will always show ~ 1 MB of leaks, no matter how many iterations
tracker.print_diff()
# Pause to check ending memory usage (I used task manager)
# ~ 60 MB with 500 iterations
# ~ 330 MB with 5000 iterations
# ~ 3.2 GB with 50000 iterations
input()
Both tracemalloc
and pympler
do not detect these leaks, and python's built in garbage collector gc
does not dispose of these items
I did some debugging. After the Python wrapper releases the IAsyncOperation COM object, the COM object still has a ref count of 1, so the COM object is not freed. I haven't been able to figure out where the extra ref count is coming from yet.
After some more digging, we can see that we first get the IAsyncOperation
object in this generated function:
template <typename D> WINRT_IMPL_AUTO(winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Media::Control::GlobalSystemMediaTransportControlsSessionMediaProperties>) consume_Windows_Media_Control_IGlobalSystemMediaTransportControlsSession<D>::TryGetMediaPropertiesAsync() const
{
void* operation{};
check_hresult(WINRT_IMPL_SHIM(winrt::Windows::Media::Control::IGlobalSystemMediaTransportControlsSession)->TryGetMediaPropertiesAsync(&operation));
return winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Media::Control::GlobalSystemMediaTransportControlsSessionMediaProperties>{ operation, take_ownership_from_abi };
}
When TryGetMediaPropertiesAsync
(from WinRT) returns, operation
already has a ref count of 2. Creating the wrapper with take_ownership_from_abi
takes ownership of one reference. This coincides with the previous observation of having 1 ref count left when we release the object.
So as far as I can tell, it is not the bindings fault that it is leaking. Maybe someone from Microsoft with knowledge of WinRT internals can enlighten us?
I found a way to fix this. You can find binaries for testing at https://github.com/dlech/xlang/actions/runs/973127446.
This is a huge improvement! From ~300MB leaked with 5000 iterations it's now down to a negligible ~15MB! In about how long can I expect to have this fix available through pip?
This issue is stale because it has been open 10 days with no activity. Remove stale label or comment or this will be closed in 5 days.