1. A 18 gigabyte heap puts it magnitudes larger than a game, presumably performance is better for smaller apps
2. I write web application servers and I very much care about GC latency
I've written 3D/Game Engines engines in languages with GC based runtimes that run at 1500fps with no GC pauses.
How to do it? Easy... don't create garbage! So yes, it is doable. PITA but doable
Certainly helps if you have a good generational GC.
That said, if you can force a compaction once a frame and it's pretty stable at a millisecond, I guess that's more than fine for a lot of stuff.
Use POD's to store data
...and use "int's" instead of references. Those index into tables of the objects. These will be opaque to the GC so it doesn't have to follow any pointers.
Specifically I mean data structures that contain only basic types and no reference types or pointers.
https://en.m.wikipedia.org/wiki/Passive_data_structure
Another important trick is to use structures of arrays, rather than arrays of structures. Well that kind of depends on the use case, but you can get huge wins in memory access if you need to scan only one field.
Same tradeoff as Row vs Column Databases.
The catastrophic problem with GC for games isn't the specific amount of time it takes up, it's that it's a periodic task; it perhaps happens once every few seconds, or even less. Which means that if you're going to have GC in your game, then you're always going to be discarding some percentage of the available CPU time so that you can survive the occasional frame where the GC runs. (or else you're having frame rate hiccups every time it runs)
Nobody in the AAA industry wants to say "Okay, lets only use 90% of the hardware's capabilities, so that those few frames in which the GC is going to run, it won't cause a frame rate hitch"; we instead engage in extraordinary (some might say 'heroic', others might say 'foolhardy') amounts of engineering effort to remove those periodic expenses, so we can move ever-closer to consistent 100% usage of the available hardware capabilities.
Sometimes that means "we're going to build this in a more difficult language which doesn't support GC", sometimes that means "we're going to figure out how to build this in this GC-enabled language without doing anything that would accidentally invoke the GC", and sometimes that means "we're going to figure out how to provide our own GC implementation which operates progressively, only spending a budgeted amount of CPU time per frame".
Personally, I skew toward that first solution. But I'm old-fashioned that way. :)
(caveat: I haven't actually worked on a 'AAA' game in the current console generation, and the majority of my industry experience was during the PS2 era. Stuff might have changed in the last few years, though I imagine that the basic principles still hold.)
60fps is roughly 16ms, for reference.
Audio is different, it's often about round trip latency (eg. from the time the sound is made, to when it is heard back) and has a few additional issues to consider. Eg. there can be phasing issues where one signal cancels out or amplifies another.
This is controlled by varying the buffer sizes, but issues can still arise due to processing latency. If the output buffer falls behind you'll hear pops as the speakers cut out.
A lot of DSP code is written to be lock-free by allocating vars when initializing so no memory allocations actually occur during updates. There are a couple great youtube talks on the subject. (look at the JUCE and CppCon channels)
Also I remember the days when any serious game developer would use Assembly, C and Pascal were only used by indie developers.
Times change.