The major difference is that the whole Go language and stdlib is simply written around patterns that avoid allocations almost magically. The simplicity of the Reader and Writer concepts is so elegant, yet powerful and doesn‘t allocate anything but a tiny reused buffer on the stack. There‘s lots and lots of other examples, but if you have to collect ten times less garbage, you‘ll be better off, even if your GC is 2x slower.
Ironically, Java HotSpot handles the use case of Reader and Writer better than Go does, since when it's not able to allocate on the stack it has fast allocation due to the use of a generational GC with bump allocation in the nursery. By virtue of the fact that it's a JIT, HotSpot can also do neat things like see that there's only one implementation of an interface and thereby devirtualize calls to that interface (allowing for escape analysis to kick in better), something Go cannot do in general as it's an AOT compiler.
Oh, the JIT devirtualizes and inlines even if there are many implementations, but only one or two at a particular callsite. This has been generalized in Graal/Truffle so you almost automatically get stuff like this (https://twitter.com/ChrisGSeaton/status/619885182104043520, https://gist.github.com/chrisseaton/4464807d93b972813e49) by doing little more than writing an interpreter.
Would you be up for writing a short post or blog post going into some anecdotal comparisons and sharing some resources?
It's better to dodge a problem, than to have a baked in problem that requires lots of really smart people to make work arounds.
I predict that over time Go's GC will evolve to become very similar to the modern GCs that Java HotSpot has. Not having generational GC was an interesting experiment, but I don't think it's panned out: you end up leaning really heavily on escape analysis and don't have a great story for what happens when you do fail the heuristics and have to allocate.
At which time the talking point will become "look how advanced and sophisticated it is!"
You never have to touch them in the Java world either, unless you like making performance worse that is.
I've never ever seen a case where fiddling with garbage collection parameters didn't make things slower.
I worked with a guy who worked on a popular java compiler, and he says the same thing. he never twiddles GC parameters either.
If the load gets too high, I just add another instance.
It's way more economical than wasting developer time fiddling with GC params.
i can only imagine someone having this view if they have never cared about latency. How have you never experienced a multi-second GC pause on default CMS settings?
I know that outside the "Java bubble" less advanced platforms are often good enough (BTW, while Java's GC ergonomics are getting better, I agree there may be more of a paradox of choice issue, but while you may not need to touch any knobs, you're also not getting low-overhead, deep production profiling and many of the other amazingly useful things you get with Java), and even inside the Java bubble we don't think Java is always the best choice for everything, but that doesn't mean that Java isn't leading in technological innovation, which was my main point.
Outside of the Java bubble, the view is quite a bit different.
All that sophistication looks like a wasted effort.
Take something as simple as admining the garbage collector. Java has a big selection of GCs, and each has their bunch of knobs for tuning. And you have to pay attention to that stuff.
After working with Go for several years, at large scale, we never once had to touch any knobs for GC. We could focus on better things. Any of the Java stuff we deploy and deal with we have this extra worry and maintenance issue.
And that's just the GC.