6 ms·
How exactly is the Go GC easier to "triage" than that of the JVM? Keep in mind that the HotSpot JVM does escape analysis just as Go does (this is sadly a very
by pcwalton 9y ago
How exactly is the Go GC easier to "triage" than that of the JVM?
Keep in mind that the HotSpot JVM does escape analysis just as Go does (this is sadly a very common misconception). The difference is that Go relies on escape analysis a lot more than the JVM does, because Go's GC has much slower allocation due to not being generational.
> man does it take a long time to figure out how Rust wants you to express what you want to express.
This goes away with experience. Certainly, most programmers get up to speed with Go faster than they do with Rust. But at this point the Rust language has totally melted away into the background for me.
- ngrilly 9y ago> The difference is that Go relies on escape analysis a lot more than the JVM does, because Go's GC has much slower allocation due to not being generational. True, but this is only a part of the story. In Java, every custom type is a reference type. This puts a lot of pressure on the GC because a lot of multiple small objects are created. Go has value types and doesn't suffer from this issue, which is a reason why having a generational GC is not that important for Go.
- littlestymaar 9y ago> This puts a lot of pressure on the GC because a lot of multiple small objects are created. … but they are allocated in the nursery, which isn't comparable to an allocation in Go (by orders of magnitude). It only makes a difference if you have a long-lived object (which must be tenured) and the difference is noticeable only if the object is small enough to be memcopied multiple times at low cost. That's why Go is only significantly faster than Java on the Go's marketing slides and not in real life.
- ngrilly 9y agoI already agreed that allocation is faster in Java than in Go. What I'm saying is that allocation speed is less critical in Go than in Java. In Java, if you allocate an object containing 10 other objects, you have to allocate 11 objects, because you only only have reference types (I know there is ongoing work to introduce value types). This is more work for the allocator, and for the GC. In Go, you allocate only once. Honestly, I'm not sure about this issue being very significant for most programs in either Go or Java. As a reminder, most C, C++ and Rust programs don't use a bump allocator either and they're doing well.
- littlestymaar 9y ago> What I'm saying is that allocation speed is less critical in Go than in Java. Indeed. In fact, Go couldn't afford not to have a generational GC without value type and Java would be really slow with Go's GC. > As a reminder, most C, C++ and Rust programs don't use a bump allocator either and they're doing well. C, C++ and Rust can't use a bump allocator since you need a compacting GC to do so. But allocations are really expensive in these languages and removing them is often the first step of optimization.
- ngrilly 9y ago> Go couldn't afford not to have a generational GC without value type And Java couldn't afford not to have value types without a generational GC ;-) > C, C++ and Rust can't use a bump allocator since you need a compacting GC to do so. But allocations are really expensive in these languages and removing them is often the first step of optimization. Agreed. This is why Go programmers use the same optimizations as C, C++ and Rust programmes is such a case (by allocating from a pool or an arena). Are you aware of cases where what is gained thanks to the bump allocator is lost because of compaction?
- littlestymaar 9y ago> Agreed. This is why Go programmers use the same optimizations as C, C++ and Rust programmes is such a case (by allocating from a pool or an arena). Yes, unfortunately in Go allocations are harder to avoid[1] than in C++ or Rust, because Go rely on escape analysis whereas objects must be manually boxed in the other languages. [1]: https://groups.google.com/forum/#!topic/golang-nuts/Vcgx7hkhd3Q https://groups.google.com/forum/#!topic/golang-nuts/Vcgx7hkh...
- ngrilly 9y agoIt's not "unfortunate". It's a known drawback of the tradeoff chosen by Go.
- 9y ago
- jashmatthews 9y ago> In Java, every custom type is a reference type. I'm not sure this is true in the actual native code produced by the JIT. HotSpot does autobox elimination.
- vitalyd 9y agoNo, it’s sadly true. EA may scalarize the allocation but this optimization falls apart very easily in Hotspot.
- pcwalton 9y ago> Go has value types and doesn't suffer from this issue, which is a reason why having a generational GC is not that important for Go. Go is in the same camp as C#/.NET here, where the generational hypothesis certainly holds and therefore .NET uses a generational garbage collector. I am certain that the generational hypothesis holds for Go as well. Not having a generational GC is a design mistake in Go. Google should fix that.
- ngrilly 9y agoIf you are interested by this topic, here is a relevant discussion on golang-nuts: https://groups.google.com/d/topic/golang-nuts/KJiyv2mV2pU/discussion https://groups.google.com/d/topic/golang-nuts/KJiyv2mV2pU/di... One of the arguments mentioned against having a generational GC is that it would make concurrent GC with low latency harder (because Go permits interior pointers, unlike Java) and slower (because Go only needs write barrier, unlike Java which needs read barrier as far as I know).
- pcwalton 9y agoIan Lance Taylor is wrong in that thread. Interior pointers don't make anything harder: this is a solved problem with card marking. Indeed, .NET has interior pointers, and it doesn't cause any problem at all. Also, I don't believe that the HotSpot GC needs to use read barriers. Read barriers are only necessary if you're concurrently compacting objects (like Azul C4 does). If you have concurrent mark-and-sweep and stop the world only during compaction phases, read barriers are unnecessary.
- vitalyd 9y agoG1 has read barriers for SATB marking. Whether a read barrier is used or not is a function of which GC is used so can’t really say “Hotspot doesn’t use read barriers”.
- pcwalton 9y agoDo you have a reference on G1 read barriers? I believe you, but I can't find any information about that from a Google search. I don't immediately see why you'd need a read barrier for snapshot-at-the-beginning. You only need to trace references from gray objects to white objects that were deleted, which is a write.
- weberc2 9y ago> How exactly is the Go GC easier to "triage" than that of the JVM? Well, firstly the language has value semantics, so in many cases you can read the source code knowing nothing about escape analysis and make inferences about where data lives. With Java, you're at the mercy of escape analysis. Further, Go's escape analysis seems simpler; I've been programming in Java on and off for a decade and I depend on profiling to hunt down allocations (except in very simple cases). This isn't the case in Go despite working with it for only a few years recreationally. > This goes away eventually I keep hearing that, but I'm 4 years into Rust and I'm still not able to be reasonably productive. I'm sure it's possible to get there, but I doubt I will ever be as productive in Rust as I am in Go for the class of applications I write.
- pcwalton 9y ago> Well, firstly the language has value semantics, so in many cases you can read the source code knowing nothing about escape analysis and make inferences about where data lives. Are you referring to heap vs. stack? That is not as simple as it seems, because of escape analysis. The only thing that Go lets you do that Java doesn't is put structs inside other structs, which is nice, but it's not a game-changer. > Further, Go's escape analysis seems simpler; I've been programming in Java on and off for a decade and I depend on profiling to hunt down allocations (except in very simple cases). This isn't the case in Go despite working with it for only a few years recreationally. I don't see how the HotSpot JVM escape analysis can be "simpler" than that of Go. They work the same way.
- weberc2 9y ago> Are you referring to heap vs. stack? That is not as simple as it seems, because of escape analysis. The only thing that Go lets you do that Java doesn't is put structs inside other structs, which is nice, but it's not a game-changer. This isn't true. I can get a contiguous slice of structs, and I can pass value types by copy without needing to reason about escape analysis at all. If I do `var foo Foo` (assuming Foo is a struct type), I know that `foo` won't escape unless I return a pointer to it (or to something in it), at which point it's subject to escape analysis. I can get quite a long way in Go without needing to reason about escape analysis at all. > I don't see how the HotSpot JVM escape analysis can be "simpler" than that of Go. They work the same way. I'm pretty sure they don't work the same way, but I don't have any examples off hand to share (it's been a while since I worked with Java). I think Java's escape analysis is more sophisticated.