“Stop” might be the right word for concurrent collectors that aren’t on-the-fly, and even then it’s kind of sketchy.
Folks often end up with concurrent GCs where the time the mutator is “stopped” is less than the time that a C or Rust mutator would be “stopped” waiting for mmap to return the next available page for an allocation that is growing the heap.
For example in FUGC (which is on the fly), the closest thing to “stopping” is a thread scanning its own stack. But what if C exceptions have to scan the stack? We don’t say C “stops” for that, even though C exception unwinding is slower than FUGC stack scans
The discussion of “stopping” and “pausing” is detrimental to a good understanding of GC overheads and *why* GCs are often a bad idea. The reason why they’re a bad idea:
- GCs will use more memory than a program that uses malloc/free.
- GCs cause memory leaks. Relatedly, GCs make it impractical to estimate max memory footprint in systems where this needs to be known statically
- GCs increase page demand (the rate at which a program asks for a page that isn’t mapped).
- GCs cause a tremendous amount of memory traffic. (A concurrent on the fly GC will have an effect on latency that looks like a pause because during the time it’s running, it’s slamming the memory subsystem).
- GCs have positive feedback. They are likely to be triggered when the system is under stress. Hence, they make stress conditions worse.
- GCs tend to be inelastic. That is, programs that suddenly free up a bunch of memory will tend to sit on it because the GC hadn’t run yet.
- GCs play poorly with real time locking and scheduling (even real time GCs that have a story for this have a bad story in the sense that schedulability analysis gets way nastier than it would be otherwise)
These are all good reasons to avoid GCs. Folks should know these reasons. Folks working on GCs should try to address these reasons directly.
Talking about “stopping” and “pausing” just confuses the situation by making it harder to see the real problems.