> Also, there's a counter-argument to this, that writing non-garbage-collected code might result in security bugs (to do with memory management).
That's falacy. Don't mix "fully manual memory management" with "I have no GC". Those are 2 totally unrelated topics. GC is one of the way to manage memory automatically. There is hundreds with their own plus-sides and downsides. Here's the least unpopular:
* Stop the world and incremental GC: Makes it easy to write simple code. Doesn't really make the lifecycle explicit so when the codebase grow larger you start to have boilerplate code to manage/close connections/files/transactions. (all toys languages, also Java and Go).
* Reference counting: Can be made fast enough by using some of the pointer 64bits to store its state. A giant problem is that all your libraries have to support compatible "smart pointer"
formats or the whole thing become unmanageable. (Modern C++)
* Contacts/Lending: Does it's job, is safe but makes the code more complex by making a lot of implicit things explicit (Rust, Modern C++)
* Explicit ownership + message passing: works well enough for GUI because just like HTML everything is less or more a tree. Also consuming messages allow "higher level" objects not to have to care about pointers. For backend code it's not very good. (Qt-C++)
* Pure functional: Everything is a copy, they get freed when out of scope. Works well for some domain, but gets in your way for more stateful constructs.
* Copy-on-Write: Pass everything by reference (and count them). Copy when they are modified, otherwise point to the real memory. Only solves half of the problem. Assuming most allocations are containers it works well enough. It doesn't attempt to solve objects management.
* Pipelining: Get input, process them, pass them on or free them. The processing should not have to dynamically allocate at all (Bash, Erlang).
* Pre-allocation: Allocate everything at compile time and use buffers big enough for your use case. (C)
* State machines: Encode the object lifecycle as a series of state changes. Release when it reaches end-of-life. Great for protocols, parsing, transactions. You usually can use code generators to generate proven safe C from an higher level format or proof. However it doesn't scale and only matches a subset of programming projects. It's also hard to learn for non EE programmers because it doesn't map perfectly on the iterative Von Neumann CPU. Easy to read but hard to write.
No-GC != security-bugs because of memory management. If a C program uses pre-allocated buffers, then the security issues is because the language is unsafe, not because it has no GC.