anacrolix / torrent

Full-featured BitTorrent client package and utilities
Mozilla Public License 2.0
5.51k stars 622 forks source link

Memory stays high after dropping torrent #930

Closed Fesaa closed 5 months ago

Fesaa commented 5 months ago

Heya!

Once a torrent is finished downloading (There is a check every 5m, that drops a torrent is torrent.Complete is true.), I drop it with torrent.Drop(), and rename it from the map I use to keep track of them. However, my application's memory usage stays up, not going down unless I restart. After a few downloads, it's at ~400mb, while if I hadn't downloaded anything it stabilizes around 25mb.

I've tried looking at a heapdump with pprof, but I understand far too little of it to figure out what might be going wrong with it.

Am I supposed to remove a torrent on the client somewhere, or close something else first? Any help would be super welcome ❤️

Thanks for the amazing project! ~ Amelia

anacrolix commented 5 months ago

Sending or linking your heap dump would be the easiest. Another option is if you can create a unit test that adds your torrents, gets the memory use up then does the Drop demonstrating the memory use (it'll be enough if you can get it to this stage for me to debug it). Thanks!

Fesaa commented 5 months ago

Heya,

For my actual application; made a heap on startup, while downloading two torrents, and then ±2m after they were both done. 20240419_mp_heaps.zip

I've also written a quick program that shows the same behaviour, but stripped down the logic a lot. Run without arguments for a little web server, with the exact same dropping logic as explained above. (curl http://127.0.0.1:8080/torrent to start them). Run with any argument, for a simple add torrents and client.DownloadAll(). 20240419_torrent_930.zip

Can change the torrents it downloads in main.go:15-18, just used two iso's, as these download relatively fast. For the example without the web server, I had 25MB startup, 60-70MB during download and 50-60MB constantly after.

Thanks for looking into this!

anacrolix commented 5 months ago

@Fesaa when you run your demo program, how are you extracting memory profiles?

anacrolix commented 5 months ago

I've added envpprof to the demo (tests/issue-930). I also found gops which might be another way to do this without adding my instrumentation package.

I removed a large allocation that occurs at init which improved memory use in general. I suspect something is holding a *Torrent but I'm not able to work out what yet. The Go tooling in this area is lacking. Memory use does decline a few minutes after dropping, I suspect either GC or goroutines holding objects are expiring (like peer connections, DHT etc.).

anacrolix commented 5 months ago

I also noticed that go-libutp creates large buffers (~1MB/conn), and they don't really shrink back down. I don't think this is a leak, after you run some torrents, you will continue to receive incoming utp connections. The buffers can be made smaller, but I remember raising them to increase throughput.

Fesaa commented 5 months ago

I read them from activity monitor (macOS) and Monitor (Gnome). I didn't add any memory profiles there sorry, but I normally just add net/http/pprof and start a net/http server in a goroutine at startup.

For my actual application I read them from docker stats, although I'm starting to suspect docker stats is reporting way more memory than it's actually using, as the sum of these is higher than what the host OS is reporting to be used. While this is obviously out of scope here, it should make the issue a lot less big than I initially described.

anacrolix commented 5 months ago

You should notice a significant improvement with the fix in master, but I don't think this issue is resolved yet (unless that satisfies you).

Fesaa commented 5 months ago

I'll try and find time some time in the following days to test this out. Thanks!

Fesaa commented 5 months ago

Heya!

I'm seeing 10MB less usage on startup, and idle until a download starts. Running the test program above on my host machine, gets me to 100-110MB, and then goes down again slowly. It's currently at 45MB here, but still going down. While the same program running in docker goes up to ~200MB, and just stays there. The container is running Ubuntu, with the same behavior in alpine. And the host is arch, but that shouldn't cause this much of a difference. And is not going down in the same time span.

image

Assuming docker is just reporting the memory usage wrong, or reading something else. I'm fine with this, it's for a personal application and restarting it every so often is no issue whatsoever for me.

anacrolix commented 5 months ago

I suspect Docker here, what version of Go are you using? There were reports in older versions of not being able to reclaim memory easily, perhaps you're running into this.

Let's close this since there's nothing else actionable here for now. We don't know if there's actually any more leaks in anacrolix/torrent, but definitely reopen it if we find something.