5 ms·
Could you explain what essential things JVM does better than Go at this point ? Does it stop the world less ? Or does it do more things in parallel ? Thanks
by leaveyou 11y ago
Could you explain what essential things JVM does better than Go at this point ? Does it stop the world less ? Or does it do more things in parallel ? Thanks
- loganekz 11y agoAlthough Go's GC is tunable to some extent, the open source HotSpot JVM are already has multiple GC implementations that you can choose based on your use case and further tune. There is also work being done in the OpenJDK project for a GC that can collect > 100GB heaps in < 10ms [1]. There are also alternative proprietary implementations available today that already have no stop the world collections [2] [1] http://openjdk.java.net/jeps/189 http://openjdk.java.net/jeps/189 [2] https://www.azul.com/products/zing/ https://www.azul.com/products/zing/
- leaveyou 11y agoFrom this "work being done in the OpenJDK project for a GC that can collect > 100GB heaps in < 10ms" I deduce that Go 1.6 is not that bad at all and 1.7 or 1.8 could even beat that easily. https://talks.golang.org/2016/state-of-go.slide#37 https://talks.golang.org/2016/state-of-go.slide#37 https://github.com/golang/proposal/blob/master/design/12800-sweep-free-alloc.md https://github.com/golang/proposal/blob/master/design/12800-...
- pcwalton 11y agoLatency isn't the only concern; you also have to look at throughput. The JVM's GC has been carefully tuned to strike a balance here. In particular, Go's GC is not yet generational from the talks I've seen, which is a large throughput loss compared to the GC of the JVM.
- geodel 11y agoIf it is carefully tuned why it needs such a big GC tuning guide and 100s of JVM flags to tune runtime. Any Java product of consequence comes with custom GC settings meaning they do not find default ones suitable. https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gc...
- pcwalton 11y agoBecause the JVM developers had customers who asked for the ability to tune the GC for their particular application. Go will receive those feature requests too. The Go developers may not be as willing to provide so many knobs (which is a position I'm entirely sympathetic to, don't get me wrong). But the settings always exist, regardless of whether Google hammers in values for them or leaves them adjustable. GC is full of tradeoffs; they're fundamental to the problem.
- mike_hearn 11y agoThe G1 collector has a single knob that is supposed to be a master knob: you pick your pause time goal. Lower means shorter pauses but overall more CPU time spent on collection. Higher means longer pauses but less time spent on collection and thus more CPU time spent on your app. Batch job? Give it a high goal. Latency sensitive game or server? Give it a low goal. There are many other flags too, and you can tune them if you want to squeeze more performance out of your system, but you don't have to use them if you don't want to.
- pjmlp 11y agoJust like C and C++ compilers have lots of options to tune all generated code. Sometime just -O2 or -O3 aren't not enough, regardless how much tuning has gone into them.
- mike_hearn 11y agoDepends what you compare it to. As I have written above, you can get low pause times with huge heaps today. In practice very few apps need such low pause times with such giant heaps and as such most users prefer to tolerate higher pauses to get more throughput. There are cases where that's not true, the high frequency trading world seems to be one, but that's why companies like Azul make money. You can get JVMs that never pause. Just not for free.
- mike_hearn 11y agoWith respect to garbage collection only and ignoring things like reliable debugging support, the primary thing it does is compaction. If your memory manager does not compact the heap (i.e. never moves anything), then this implies a couple of things: 1. You can run out of memory whilst still technically having enough bytes available for a requested allocation, if those bytes are not contiguous. Most allocators bucket allocations by size to try and avoid the worst of this, but ultimately if you don't move things around it can always bite you. 2. The allocator has to go find a space for something when you request space. As the heap gets more and more fragmented this can slow down. If your collector is able to move objects then you can do things generationally which means allocation is effectively free (just bump a pointer). In the JVM world there are two state of the art collectors, the open source G1 and Azul's commercial C4 collector (C4 == continuous compacting concurrent collector). Both can compact the heap concurrently. It is considered an important feature for reliability because otherwise big programs can get into a state where they can't stay up forever because eventually their heap gets so fragmented that they have to restart. Note that not all programs suffer from this. It depends a lot on how a program uses memory, the types of allocations they do, their predictability, etc. But if your program does start to suffer from heap fragmentation then oh boy, is it ever painful to fix. The Go team have made a collector that does not move things. This means it can superficially look very good compared to other runtimes, but it's comparing apples to oranges: the collectors aren't doing the same amount of work. The two JVM collectors have a few other tricks up their sleeves. G1 can deduplicate strings on the heap. If you have a string like "GET" or "index.html" 1000 times in your heap, G1 can rewrite the pointers so there's only a single copy instead. C4's stand-out feature is that your app doesn't pause for GC ever, all collection is done whilst the app is running, and Azul's custom JVM is tuned to keep all other pause times absolutely minimal as well. However IIRC it needs some kernel patches in order to do this, due to the unique stresses it places on the Linux VMM subsystem.
- colin_mccabe 11y agoWhile I agree that compaction is desirable in theory, empirically it's not really necessary. For example, there are no C/C++ malloc/free implementations that compact, because compaction would change the address of pointers, breaking the C language. Long-lived C and C++ applications seem to get by just fine without the ability to move objects in memory. Java code also tends to make more allocations than Go code, simply because Java does not (yet) have value types, and Go does. This isn't really anything to do with the GC, but it does mean that Java _needs_ a more powerful GC just to handle the sometimes much greater volume of allocations. It also makes Java programmers sometimes have to resort to hacks like arrays of primitive types (I've done this before). People like to talk about how important generational GC is, and how big a problem it is that Go doesn't have it. But I have also seen that if there is too high a volume of data in the young-gen in Java, short-lived objects get tenured anyway. In practice, the generational assumption isn't always true. If you use libraries like Protobuffers that create a ton of garbage, you can pretty easily exceed the GC's ability to keep up with short-lived garbage. I'm really curious to see how Go's GC works out for big heaps in practice. I can say that my experience with Java heaps above 100 GB has not been good. (To be fair, most of my Java experience has been under CMS, not the new G1 collector.)