> Haskell has a somewhat unique garbage collector based on the fact that all data is immutable. They can take shortcuts because older references can't refer to newer references.
When you don't mutate in OpenJDK you get essentially the same. Much of the cost of a modern GC (OpenJDK's G1, and soon probably ZGC, too) is write barriers, that need to inform the GC about reference mutations. If you don't mutate, you don't pay that cost. This is partly why applications that go to the extreme in the effort not to allocate and end up mutating more, might actually do worse than if they'd allocated more with OpenJDK's newer GCs.
In fact, OpenJDK's GCs rely heavily on the assumption that old objects can't reference newer ones unless explicitly mutated, and so require those barriers only in old regions.
Yes (well, sort of; you could use the Epsilon GC, which is essentially a no-op GC), but I once read of a library that allocated all of its objects at initialisation and claimed to be "GC-neutral", i.e. not add any memory management burden regardless of the GC chosen by the application using it. They were very surprised when they tried running their benchmarks with G1. OpenJDK's modern GCs are optimised for "reasonable behaviour." Allocate too much and you suffer; allocate too little (by which I mean reuse objects and mutate them a lot) and you also suffer. But as long as your behaviour is in the broad more-or-less normal range, you get some really good performance, which is only getting better with each release.
When you don't mutate in OpenJDK you get essentially the same. Much of the cost of a modern GC (OpenJDK's G1, and soon probably ZGC, too) is write barriers, that need to inform the GC about reference mutations. If you don't mutate, you don't pay that cost. This is partly why applications that go to the extreme in the effort not to allocate and end up mutating more, might actually do worse than if they'd allocated more with OpenJDK's newer GCs.
In fact, OpenJDK's GCs rely heavily on the assumption that old objects can't reference newer ones unless explicitly mutated, and so require those barriers only in old regions.