5 ms·
I spent a lot of the past six years dreaming nightmares of the unpredictability of CMS and, to a lesser extent, G1. So it's been with extreme skepticism that I'
by jakewins 10y ago
I spent a lot of the past six years dreaming nightmares of the unpredictability of CMS and, to a lesser extent, G1. So it's been with extreme skepticism that I've received the Go teams claims of how the Go GC performs.
Maximum STW of <1ms? Uhh, ok. The best-of-breed open Java GCs have targets of <100ms on big heaps, 1ms ceiling sounded insane.
However, diving into it, the problems solved by the Go GC and those solved by the Java GC are very different in at least two major ways:
- Go has substantially less dynamic runtime behavior; there's no JIT, no runtime loading of new code, very limited reflection, &c.
- The Go GC does not do heap compaction
Both of these things mean: a) Objects do not randomly move about the heap and b) code does not randomly redefine itself.
Which, the Go team have made their bed, just like the Java team have, and the Go bedroom looks a heck of a lot more comfy to sleep in. Building a fast GC for Go is a massively simpler problem than building a GC for Java, hence the claims they are making of worst-case scenarios are not completely off the wall.
Now, that doesn't mean they should be taken at face value. Notably, the Go GC still has to decide when to start a cycle, and still needs to stop the application if it runs out of heap due to starting a cycle too late. I would suspect that you can construe a test case with a very large heap and a random and extremely varying allocation rate that could cause the Go runtime to explode just fine.
Likewise, the time to complete a GC cycle is (at best) linear to the heap size of Go - as the heap grows, the time to reclaim memory does as well, meaning you need to keep larger margins in the heap for floating garbage waiting to be reclaimed.
- SomeCallMeTim 10y agoYou've got some great points, but there's one more dimension to why GC in Java hurts performance as much as it does: Java, by the design of its standard libraries and by the conventions that many (most?) Java developers use, just tends to allocate memory for every single operation. I was writing the Java part of a cross-platform game engine once, and I was trying to eliminate all dynamic allocations during the game (front-loading all allocations and then never touching the heap). Did some profiling and determined I'd missed something. The allocation turned out to be in a call to get the current system timer (in milliseconds, if I recall). I was calling a Java function that returned a Java long int, probably straight from an OS call that returned a 64-bit int, and somewhere in that call it was allocating an Object. Ripped out the Java library call, replaced it with a JNI call to a two line C function that returned the result of the corresponding OS call. No more dynamic allocations. It's just part of the philosophy of Java to allocation things everywhere. So it's not only hard to make a GC fast for architectural reasons; the GC also tends to be worked harder by the way Java code tends to be written.
- jakewins 10y agoExcellent points. One of the emotional appeals of Go, to me, is the community behavior and mindset. Go code very often "feels" like a low level language, written by someone that was considerate of the underlying machine. You're also hitting another nerve: The horrific world of allocation tracking in Java. While your case sounds like a legitimate thing, so many times I've been in that situation and been tricked by my tools to optimize the wrong path. Most popular allocation tracking tools, like YourKit, turn off escape analysis. Obviously, when you do that, stupid things like boxed primitives will show up as your main sources of heap allocation even though Hotspot would stick those suckers on the stack in a heart beat. Eg: Here's a program running without YourKit allocation tracking: https://twitter.com/jakewins/status/707759792547233792 https://twitter.com/jakewins/status/707759792547233792 And here's the same program with it on: https://twitter.com/jakewins/status/707754868497231872 https://twitter.com/jakewins/status/707754868497231872 Still makes me furious - what in the world am I supposed to do with an allocation tracking tool that creates millions of false positive allocations, drowning out the thing I care about? Gah.
- pjmlp 10y agoWhich actually could have been fixed since version 1.0, had the language designers added support for value types instead of trying to retrofit them into Java 10. I like the language, but it did a bit of left turn by not adopting the features of Oberon(-2), Modula-3, Component Pascal or Eiffel in regards to value types and native AOT compilation.