You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A potential memory leak has been observed in aiohttp when handling streaming responses. The following script reproduces the issue by continuously reading a response stream from an aiohttp server. After the client disconnects due to ServerDisconnectedError, garbage collection (gc) still reports lingering ClientResponse objects, suggesting a resource leak due to cyclic references in traceback objects.
To Reproduce
Run the following script.
Observe the output for lingering ClientResponse objects, which are visualized using objgraph.
importasyncioimportaiohttpfromaiohttpimportwebimportgcfromtimeimporttimeimportobjgraphgc.set_debug(gc.DEBUG_LEAK)
defget_garbage():
result= []
gc.collect()
forobjingc.garbage:
obj_name=type(obj).__name__result.append(f'{obj_name}')
ifobj_namein ('ClientResponse',):
print('ClientResponse not collected!')
objgraph.show_backrefs(
obj,
max_depth=30,
too_many=50,
filename=f"/tmp/{int(time() *1000)}err_referrers.png",
)
returnresultclassClient:
def__init__(self):
self.session=aiohttp.ClientSession()
self.response=Noneasyncdeffetch_stream(self, url):
try:
self.response=awaitself.session.get(url)
ifself.response.status==200:
whileTrue:
chunk=awaitself.response.content.readexactly(6)
print(f'received: {chunk.decode().strip()}')
else:
print(f'response status code: {self.response.status}')
except (
aiohttp.ClientConnectorError,
aiohttp.ServerDisconnectedError,
aiohttp.ClientPayloadError,
asyncio.IncompleteReadError
) ase:
print(f'connection error ({type(e).__name__})')
exceptExceptionase:
print(f'unexpected error: {e}')
finally:
self.response=None# Explicitly clear responseself.session=None# This should close the session, but memory leak persists due to traceback references.asyncdefstream_handler(request):
writer=request.transportifwriter:
writer.close() # Forcefully closing connectionreturnweb.Response()
asyncdefmain():
app=web.Application()
app.router.add_get('/stream', stream_handler)
runner=web.AppRunner(app)
awaitrunner.setup()
site=web.TCPSite(runner, 'localhost', 8080)
awaitsite.start()
client=Client()
client_task=asyncio.create_task(client.fetch_stream('http://localhost:8080/stream'))
awaitclient_taskawaitasyncio.sleep(0.5) # Allow time for cleanupprint(f'Garbage objects: {get_garbage()}')
awaitrunner.cleanup()
if__name__=='__main__':
asyncio.run(main())
Expected behavior
ClientResponse should be properly garbage collected after the session is closed.
No lingering references should prevent cleanup.
Logs/tracebacks
Observe the output for lingering ClientResponse objects, which are visualized using objgraph.
Python Version
3.12.9
aiohttp Version
3.11.11
multidict Version
6.1.0
propcache Version
0.2.0
yarl Version
1.17.1
OS
Ubuntu 22.04.5 LTS
Related component
Client
Additional context
Fixed Version FixClientResponse (Memory Leak Resolved)
Describe the bug
A potential memory leak has been observed in aiohttp when handling streaming responses. The following script reproduces the issue by continuously reading a response stream from an aiohttp server. After the client disconnects due to ServerDisconnectedError, garbage collection (gc) still reports lingering ClientResponse objects, suggesting a resource leak due to cyclic references in traceback objects.
To Reproduce
Expected behavior
Logs/tracebacks
Python Version
3.12.9
aiohttp Version
3.11.11
multidict Version
6.1.0
propcache Version
0.2.0
yarl Version
1.17.1
OS
Ubuntu 22.04.5 LTS
Related component
Client
Additional context
Fixed Version FixClientResponse (Memory Leak Resolved)
Code of Conduct
The text was updated successfully, but these errors were encountered: