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
python3.9 without uvloop doesn't leaks memory (or noticeably smaller).
python3.11+ (and others?) leaks memory A LOT under load (with or without uvloop) - up to +2gb per every test!
test commands:
ab -n50000 -c15000 -r https://127.0.0.1/
(apt install apache2-utils)
Can confirm that the issue exists - Python 3.11, and uvloop 0.17.0, when trying to set up many client SSL connections that sometimes reconnects - even directly with asyncio.create_connection, and it's cleaned up not properly in some place.
Sometimes it leaked several megabytes per second for me.
I was able to track this by attaching to a running python process via gdb, and when it leaked, I put a catchpoint for brk syscalls, and examined 40 sequential breakpoint events, when all were related to /usr/lib/python3.10/ssl.py.
(gdb) py-bt
Traceback (most recent call first):
File "/usr/lib/python3.10/ssl.py", line 917, in read
v = self._sslobj.read(len)
<built-in method buffer_updated of uvloop.loop.SSLProtocol object at remote 0xffff758f90c0>
<built-in method run of _contextvars.Context object at remote 0xffff758c7340>
File "/home/ubuntu/py-core/env/lib/python3.10/site-packages/aiohttp/web.py", line 544, in run_app
loop.run_until_complete(main_task)
File "/home/ubuntu/py-core/server_app.py", line 39, in <module>
web.run_app(app_factory(), loop=LOOP, host=args.host, port=args.port)
(gdb)
Also, a gc.get_objects() count was not actually growing, that's why I suppose that this is related a lower-level native implementation.
Unfortunately I am really bad at this type of low level stuff. :-( If you suspect a leak in the C accelerator, you can disable it and see if the leak goes away. (Or at least becomes less severe.)
I actually was able to reproduce this leak isolated several days ago, but it leaked for only ~5 megabytes in several hours. I adopted a repro script from this comment to the aiohttp bug mentioned above to be ready for run, and ran it for about 4 hours.
The initial memory consumption (RSS) had initially stabilised on 258120 bytes, but after several hours increased to ~264000. Hope it's the same bug.
Unsure if this is helpful at all or related but I came across this issue Kludex/uvicorn#2078 in which in the thread it is discussed/concluded that the issue of memory not being released is not isolated to uvicorn but seen in granian/gunicorn/hypercorn and as a result could be interpreter level (apologies for butchering the summary). Thread has some great charts/analysis though it is on the server level, however the different implementations w.r.t granian and uvicorn can help approximate where the issue might be surfacing if it's related. Example repo: https://github.com/Besedo/memory_issue/tree/main by @EBazarov Apologies in advance if it is unrelated and will remove the comment/create one in the right place.
gi0baro:
Given everything we stated in the upper replies, I suspect there's no much to do about this. Given the number of frameworks and servers tested, it just seems the Python interpreter is the culprit here, as even if the heap memory gets released, the RSS remains high.
This should be highlighted as I (and many others) could've avoided a lot of headache had I not upgraded my applications to the latest Python (or used a different language altogether).
I can absolutely relate to this because we ended up rewriting the whole app in Rust. One of the main reasons was this bug (and async libs being quite buggy in general). It took me days to debug what's going on and finally landed in here.
In my case MALLOC_TRIM_THRESHOLD_=131072 seems to work. Those vertical lines are project updates. I'm using Python 3.11 and Django 5.0.
I've not seen memory consumption higher than 450 MB, but I got rid of media passing through Django server, just JSON requests and responses.
This is helpful @stalkerg assuming the glibc maintainers actually get to reading the thread they will eventually come up with something, hopefully not a won't-fix
@geraldog Many thanks for your reply.
It looks like a great workaround but to be fully honest I am not familiar enough with the low level package dependencies to completely understand the consequences.
I assume most python environments (docker containers like f.e. python-3.11-slim etc) and many python packages depend on c support.
How exactly do you recommend to replace glibc then? is there a compatible package as drop-in replacement that you can recommend? Or do you use other base images to create the runtime environments?
Again many thanks for your help. I really appreciate it a lot.
How exactly do you recommend to replace glibc then? is there a compatible package as drop-in replacement that you can recommend? Or do you use other base images to create the runtime environments?
Try an Alpine Linux image. People in this thread have had success in working around this bug with it. It's built around musl libc instead of GNU libc.
Hi! @geraldog could you please summarize what's happening there and if we should close this issue if the problem is not on CPython's end? Thank you in advance.
This is definitely not a CPython issue and I believe the issue should therefore be closed.
The observed "memory leak" is actually a glibc quirk or optimization as discovered by @stalkerg where memory allocated is never reclaimed with a free(), keeping the previously allocated memory in a pool for later re-use.
Some users like me have reported success with setting the env var MALLOC_TRIM_THRESHOLD_ to a low value as a workaround, while other users proved the issue is not reproducible with musl libc based system like Alpine.
Thank you for the answer, @geraldog!
I'm closing this as not planned, since the problem is not at CPython's end.
Please ping me and I'll reopen this issue(or maybe it would be better to create a new one, because this one has had quite big discussion..) if you find something on CPython's side related to this issue.
Thank you all for spending time finding the root cause!
Activity
selevit commentedon Nov 17, 2023
Can confirm that the issue exists - Python 3.11, and uvloop 0.17.0, when trying to set up many client SSL connections that sometimes reconnects - even directly with
asyncio.create_connection, and it's cleaned up not properly in some place.Sometimes it leaked several megabytes per second for me.
I was able to track this by attaching to a running python process via
gdb, and when it leaked, I put a catchpoint forbrksyscalls, and examined 40 sequential breakpoint events, when all were related to/usr/lib/python3.10/ssl.py.Also, a
gc.get_objects()count was not actually growing, that's why I suppose that this is related a lower-level native implementation.arhadthedev commentedon Nov 18, 2023
@1st1, @asvetlov, @graingert, @gvanrossum, @kumaraditya303 (as asyncio module experts)
gvanrossum commentedon Nov 18, 2023
Unfortunately I am really bad at this type of low level stuff. :-( If you suspect a leak in the C accelerator, you can disable it and see if the leak goes away. (Or at least becomes less severe.)
gvanrossum commentedon Nov 18, 2023
OTOH if the problem is in the SSL code, we need a different kind of expert.
gvanrossum commentedon Nov 18, 2023
Finally please figure out if this is in uvloop or not. If it’s tied to uvloop this is the wrong tracker.
selevit commentedon Nov 19, 2023
Unfortunately, this bug was reproduced only in our production, and we weren't able to cause it in other environments.
It's already mitigated, as the root cause was in poor stability of connections.
I don't think I would be able to reproduce it again in production without
uvloop, because it caused a lot of harm for our system.But ready to assist and provide more details, if anyone wants to ask me some clarifying questions about it.
Also, there are several related tickets, and I'm not sure if they are all about
uvloop:aiohttpissue, they were able to mitigate it somehow, but not sure if it was the same bug.selevit commentedon Nov 19, 2023
I actually was able to reproduce this leak isolated several days ago, but it leaked for only ~5 megabytes in several hours. I adopted a repro script from this comment to the aiohttp bug mentioned above to be ready for run, and ran it for about 4 hours.
The initial memory consumption (RSS) had initially stabilised on
258120bytes, but after several hours increased to~264000. Hope it's the same bug.gvanrossum commentedon Nov 19, 2023
Could you find a reproducer without aiohttp?
Faolain commentedon Nov 27, 2023
Unsure if this is helpful at all or related but I came across this issue Kludex/uvicorn#2078 in which in the thread it is discussed/concluded that the issue of memory not being released is not isolated to uvicorn but seen in granian/gunicorn/hypercorn and as a result could be interpreter level (apologies for butchering the summary). Thread has some great charts/analysis though it is on the server level, however the different implementations w.r.t granian and uvicorn can help approximate where the issue might be surfacing if it's related. Example repo: https://github.com/Besedo/memory_issue/tree/main by @EBazarov Apologies in advance if it is unrelated and will remove the comment/create one in the right place.
gi0baro:
83 remaining items
gyula-lakatos commentedon Jul 2, 2024
I can absolutely relate to this because we ended up rewriting the whole app in Rust. One of the main reasons was this bug (and async libs being quite buggy in general). It took me days to debug what's going on and finally landed in here.
fifdee commentedon Jul 3, 2024
In my case MALLOC_TRIM_THRESHOLD_=131072 seems to work. Those vertical lines are project updates. I'm using Python 3.11 and Django 5.0.

I've not seen memory consumption higher than 450 MB, but I got rid of media passing through Django server, just JSON requests and responses.
nikumar1206 commentedon Sep 29, 2024
Are there any updates on this issue? Did anyone open up a bug report with the glibc maintainers?
This issue has made trying to run a long-running Python server nearly impossible.
stalkerg commentedon Oct 7, 2024
@nikumar1206 I did attempt a few moments ago https://sourceware.org/pipermail/libc-help/2024-October/006791.html but not sure.
geraldog commentedon Oct 7, 2024
This is helpful @stalkerg assuming the glibc maintainers actually get to reading the thread they will eventually come up with something, hopefully not a won't-fix
1st1 commentedon Oct 8, 2024
CodeToFreedom commentedon Feb 21, 2025
Hello,
we have the same problem. Did anyone find a solution or workaround in the meantime? Many thanks for your efforts.
geraldog commentedon Feb 22, 2025
Ditching glibc entirely seems to be a solution after all...
CodeToFreedom commentedon Feb 22, 2025
@geraldog Many thanks for your reply.
It looks like a great workaround but to be fully honest I am not familiar enough with the low level package dependencies to completely understand the consequences.
I assume most python environments (docker containers like f.e. python-3.11-slim etc) and many python packages depend on c support.
How exactly do you recommend to replace glibc then? is there a compatible package as drop-in replacement that you can recommend? Or do you use other base images to create the runtime environments?
Again many thanks for your help. I really appreciate it a lot.
geraldog commentedon Feb 22, 2025
Try an Alpine Linux image. People in this thread have had success in working around this bug with it. It's built around musl libc instead of GNU libc.
Eclips4 commentedon Mar 11, 2025
Hi!
@geraldog could you please summarize what's happening there and if we should close this issue if the problem is not on CPython's end? Thank you in advance.
geraldog commentedon Mar 11, 2025
Hi @Eclips4
This is definitely not a CPython issue and I believe the issue should therefore be closed.
The observed "memory leak" is actually a glibc quirk or optimization as discovered by @stalkerg where memory allocated is never reclaimed with a free(), keeping the previously allocated memory in a pool for later re-use.
Some users like me have reported success with setting the env var MALLOC_TRIM_THRESHOLD_ to a low value as a workaround, while other users proved the issue is not reproducible with musl libc based system like Alpine.
Eclips4 commentedon Mar 12, 2025
Thank you for the answer, @geraldog!
I'm closing this as not planned, since the problem is not at CPython's end.
Please ping me and I'll reopen this issue(or maybe it would be better to create a new one, because this one has had quite big discussion..) if you find something on CPython's side related to this issue.
Thank you all for spending time finding the root cause!