Repository: Pycord-Development/pycord

Issue:
Random RAM spikes, repetetive
### Summary

RAM spike go brr, then silence, then brrr again

### Reproduction Steps

I have my entire reproduction here:
https://github.com/AlexFlipnote/PyCord_RAMDebug

### Minimal Reproducible Code

_No response_

### Expected Results

RAM goes slightly up, as expected, but not too much of it

### Actual Results

RAM spikes up with 30-100 MB, then goes silent for a while, to then spike again, never lowering the RAM at all.

### Intents

Listed inside the repo. linked above

### System Information

```sh
alexflipnote@xelaservur:~$ python -m discord -v
- Python v3.10.0-final
- py-cord v2.0.0-alpha
    - py-cord pkg_resources: v2.0.0a4338+g7e4ce979
- aiohttp v3.8.0
- system info: Linux 4.19.0-14-amd64 #1 SMP Debian 4.19.171-2 (2021-01-30)
```

### Checklist

- [X] I have searched the open issues for duplicates.
- [X] I have shown the entire traceback, if possible.
- [X] I have removed my token from display, if visible.

### Additional Context

_No response_


Discussion:
Some additional conversations for context:
[Image1](https://i.alexflipnote.dev/9WKpBWZ.png) [Image2](https://i.alexflipnote.dev/8iw8dbw.png)
Update: Forgot that the default `.gitignore` for Python removes all `.log` files. Added all the logs inside the `./dumps` folder now.
brrrrrrrrrr


Update: I let the bot stay alive for almost 4 days in a row, the RAM has built up drastically. 
![image](https://user-images.githubusercontent.com/19587382/141656505-d57c1a1b-486f-4565-8cc1-6c98a5135057.png)

Whenever the spikes go up, `asyncio.Lock` just climbs more and more. Here's one of the images with a chart. Almost all 4,000 instances I found on the bot has the same exact thing. 

Note; this huge memory leak started whenever I added buttons to my bot.

![leak2](https://user-images.githubusercontent.com/19587382/142744443-144a0f55-9856-49a1-a006-a4bb8f7a517e.png)

Here are some dumps I did, 1 being from boot and 4 being the 4th day after boot.
[1.txt](https://github.com/Pycord-Development/pycord/files/7575542/1.txt)
[2.txt](https://github.com/Pycord-Development/pycord/files/7575543/2.txt)
[3.txt](https://github.com/Pycord-Development/pycord/files/7575544/3.txt)
[4.txt](https://github.com/Pycord-Development/pycord/files/7575545/4.txt)


## More information for possible leak
`/discord/webhook/async_.py > AsyncWebhookAdapter > request`
![](https://i.alexflipnote.dev/8iYjRAW.png)
This dict collector never goes down for each interaction, it keeps going up and up.

`/discord/http.py` uses a similar approach as to the Webhook class method, however it manages to decrease over time as well
![](https://i.alexflipnote.dev/7An5kjn.png)

Here's a video of when I use buttons, it just keeps adding it up and up, to a point where it can reach beyond 4,000 locks inside the dict, as seen with the screenshot here.
![](https://user-images.githubusercontent.com/19587382/142744443-144a0f55-9856-49a1-a006-a4bb8f7a517e.png)

[Video example of Webhook async lock going always up](https://i.alexflipnote.dev/4KBkhNQ.mp4)

Resolving patch:
diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py
--- a/discord/webhook/async_.py
+++ b/discord/webhook/async_.py
@@ -29,6 +29,7 @@
 import asyncio
 import json
 import re
+import weakref
 
 from urllib.parse import quote as urlquote
 from typing import Any, Dict, List, Literal, NamedTuple, Optional, TYPE_CHECKING, Tuple, Union, overload
@@ -98,7 +99,7 @@ async def __aexit__(self, type, value, traceback):
 
 class AsyncWebhookAdapter:
     def __init__(self):
-        self._locks: Dict[Any, asyncio.Lock] = {}
+        self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
 
     async def request(
         self,
diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py
--- a/discord/webhook/sync.py
+++ b/discord/webhook/sync.py
@@ -36,6 +36,7 @@
 import json
 import time
 import re
+import weakref
 
 from urllib.parse import quote as urlquote
 from typing import Any, Dict, List, Literal, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union, overload
@@ -94,7 +95,7 @@ def __exit__(self, type, value, traceback):
 
 class WebhookAdapter:
     def __init__(self):
-        self._locks: Dict[Any, threading.Lock] = {}
+        self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
 
     def request(
         self,


Test patch: