Every time I've prototyped something on a GC'd language (java, actionscript, javascript, ..) I soon find myself optimizing memory allocations and not making the game because the GC causes too much overhead and/or causes too long pauses and therefore makes the game feel miserable. It doesn't matter if there are some theoretical GC models which have < 1ms pauses with gigabytes of heap data, as the current implementations don't seem to be anywhere near that.
Nowadays I prefer to use C not because I like malloc, but because it makes handling different memory allocation schemes trivial (unlike C++). Furthermore, in this semi-real-time case the performance of malloc is irrelevant because when you want to be fast and low-latency, you pre-allocate everything and use lightweight functions to further distribute memory from those blocks.
In conclusion, I've found that manual memory management lets me focus more on the task I have in hand while GC don't.
(I'm an Azul customer and Zing user)
So discussion about "GC" in the abstract are useless, discussion about the GC in Go 1.5 are absolutely worth it, and then you can find real-world stories about GC failures.
"Hardware provisioning should allow for in-memory heap sizes twice as large as reachable memory and 25% of CPU cycles". http://llvm.cc/t/go-1-4-garbage-collection-plan-and-roadmap-...
As far as I know, that collector is not a copying collector. I also know that all JDK GCs have significant memory overhead (not sure about the Azul one).
So they need to scan the complete heap, while a good copying collector (e.g. a two-finger Cheney with forwarding pointers) would only need to scan the stack and some roots. My GC needs ~4ms on normal heap sizes, the fastest M&S GC's need ~150ms. But I haven't found a good version besides the Azul one, which works fine threaded.
Memory overhead: A non-copying GC has none. Lookup a GC book or explanation. Refcounts have plenty: 1 word per object. malloc has plenty for its free-list management, growing with the heap size.
A semi-space GC is different as it reserves for every heap segment a mirror segment. A fast version reserves max heap and divides it by 2, so can use max 2GB of 4GB. A normal version can do that incrementally.
Java has a huge memory overhead from the kernel and run-time alone, not so the GC, but since they have various swappable GC's you need space for that. You can write better and smaller run-times with GC which do run circles around Java, .NET, Go or Ruby. As I said mine needs ~4ms, the fastest java is ~20 - 150ms. v8 has a good one, but I don't know their stats out of my head.
Do you have any idea what could make the Go folks ask for "in-memory heap sizes twice as large as reachable memory"? This seems to be completely at odds with what you are saying.
Well, look at the arguments. GC advantages:
What you got wrong: significant memory and CPU overhead.If you compare the memory overhead and complexity of refcounts with malloc to a non-semi-space GC, you'll be surprised. malloc is more complex and worse in it's CPU overhead, refcounts in every data cell are a huge overhead. GC's got it better.
Where GC's have a memory overhead (copying collectors), they do it on purpose, on machines which do have enough memory. Those copying collectors are the fastest, but cannot be used on small devices. With not enough RAM, you just use a trivial Mark&Sweep, which is much more trivial than your malloc implementation and manual refcounts.
The only real field where you cannot use a GC is, when you cannot tolerate pauses, as in real-time, latency critical apps. But even there exist incremental GC's (like boehm) with real-time characteristics. And when you need immediate destruction of objects, when they go out of scope, and not when the GC decides to destroy them later on. This can be solved by the compiler, but usually isn't.
Of course people always walk away from GC and avoid it like a plague. GC people on the other hand feel memory is too important to be trusted to programmers. We had these discussion for decades.