Telegram, from a quick AI query is known for that apparently. It caches everything in memory. Note this is a "cross platform" technique most likely. When a Linux Native application wants to cache stuff it will usually use a memory mapped temp file. This allows linux kernel to manage the paging and caching and swap etc.
Browser tabs add up in memory quite a bit.
Memory is funny if you are true honest with yourself. You could use less of it, but it's available, so you use it. Then you complain that modern software is happy to let you use it and not optimise itself out of it's real market. Once you are comfortable using it, you get annoyed when you find yourself on a memory constrained system like an old box repurposed. "Why's it so bloaty?"
It's not that software kept requiring more and more memory. It's that hardware folks kept expanding it under us and we decided to use it.
Also, not being memory constrained makes coding easier. Consider something as low level as fetching a value like "Download % complete" from the download background process. You could create accessors/IPC to query each element/field so that calls to these are quick, small, fast with little to no allocation required. OR You could just write one accessor/IPC that returns the whole contents of the download record with all 25 fields and sublists in one monolithic operation. You only need one of them. You might need several dozen integration points for the former, more memory constrainted variant.
Thats one example out of hundreds. Holding more than one context or task in memory is probably the other big one. Old applications tended to tear parts down and rebuild new ones when switch contexts, modern applications just make them exist all at once so you can context switch yourself. Like having many different views open on teh same schematic in the same instance of the editor, versus 10-20 years ago you would need to open who new applications for different views. (Browsers originally had a single window per instance, then multi-windows, now multi-windows, tabs, groups, workspaces etc.
Validation and constraints. When you are memory limited, even today, you will set "lower" limitations on the length and size of the contents. This requires more validation, and more work avoiding over runs. It also creates difficulties later when those constraints need to change for business requirements and that can be painful with databases etc. So with more memory and performance available those constraints get set higher. Values are allowed to be longer. This enabled the user more and lowers the complexity of the software.
p.s. after restarting that Firefox and restoring the previously opened windows and tabs it uses half the previous amount, about 3 GB. Programming skills my ass...
Do not confuse memory leaks with memory use. Restarting firefox destroys much of its in-memory runtime state. There is a fairer argument towards how "sloppy" they are with such in memory caches.
One example, in the case of firefox, a lot of it's profile data is stored in an SQLite database, but that file is memory mapped and "in memory accessed". However, it is, because the linux kernel is, "lazy loaded". So the pages will not be read into RAM until they are accessed. After running the browser for a few hours it's back to 2Gb.