Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

Most other sophisticated GCs (e.g. .NET and Java's) can obtain pause times in a similar range for generation 0 collections. So if GC is the reason you don't want to use one of those you'd probably be more interested in improvements in worst case pause times. Go is however very good at avoiding unnecessary object allocation and doesn't need a JIT so it may still be closer to what you need than those languages.


sort by: page size:

It's all relative. Go is optimized for pause times and beats all other mainstream runtimes for that. JVM GC is very configurable and can be optimized for throughput, pause times, heap size, fragmentation, or whatever you want, and can beat Go's for pretty much all of them except pause times. Both have very smart people working on them and the techniques are well established, so it's about tradeoffs. (An oldish post but I think still relevant: https://blog.plan99.net/modern-garbage-collection-911ef4f8bd...)

That said, Go's focus may be the right one for many use cases. On a high throughput service where you would assume you want to optimize for throughput, 100 ms pause times can wreak havoc because they're unpredictable and can cause work queues to explode and such. This isn't easily mitigated by load balancing. Whereas "less efficient" GC is at least predictable and you can just add a server to balance that extra work.


> MO the big GC related issue is lack of determinism especially in the management of external resources.

This isn't a property of GCs per se; it's just that most GCs are optimized for throughput. Go's GC's pause times are on the order of 1ms, which might not be appropriate for every application, but it's probably fine for soft-realtime systems. There are a lot of other levers one could imagine as well, like semantics for demarcating critical sections.


Properly written Go code (or even Java for that matter) will try to minimize allocations. For Java, unless I am mistaken pause-less GC is only offered by Azul - $$

Go also doesn’t have huge GC pauses (and moreover idiomatic Go generates very little garbage)and I have a hard time seeing how GC pauses would contribute so significantly. Java also allegedly has a very low latency collector.

I want to see a source on this. Golang's GC is often touted as better than Java's but every real world benchmark I've seen shows that it sacrifices a lot of throughput for low pause times, essentially by running much more often.

Java's new garbage collectors, ZGC and Shenandoah, have average pause times of 0.3 milliseconds on heaps less than 4GB. I find it unlikely that another language has pause times shorter than that given the sheer amount of work put into Java GC over the years


Comparing Go's GC to one of the many specialized fine-tunned Java GCs is pointless, I find.

I'm sure you're also aware that recent Go's GC pauses are sub millisecond for most use cases:

"We now have an objective of 500 microseconds stop the world pause per GC cycle." - 2018 Go team

https://blog.golang.org/ismmkeynote

My personal experience with microservices is to expect STW pauses in the 350 microsecond range. The best part is that it requires zero tunning or developer's attention while still being light on memory usage. Can't say the same for Java's default GC.


Much work has gone into making Go GC pauses as quick as possible, so it might be in a better position relative to other GC'd languages.

Also, you might be able to toggle the GC off, and invoke manually with runtime.GC() at opportune moments.


Go achieves those low pause times by allocating 2x memory to the heap than it's actually using. There's no free lunch with GC.

Friendly PSA that modern Go has GC pauses under 1ms in almost all cases. That may still be a problem for your application, but it is a far cry from what people expect from other GC languages like Java.

Go’s GC is low latency and it allows you to explicitly trigger a collection as well as prevent the collector from running for a time. I would wager that the developer time/frustration spent taming the GC would be more than made up for by the dramatic improvement in ergonomics everywhere else. Of course, the game dev libraries for Go would have to catch up before the comparison is valid.

I would wager that Java's GC is better just for the simple fact that it has a whole bunch of knobs you can turn based on your workload. I've investigate an issue with a very time Go program, and the performance of it was being harmed by the Garbage Collector.~600ns GC doing 2k GC/s per second- ~12% of its time was spent garbage collecting. The solution was to turn the GOGC to 37- this yielded the best results but only increased performance 5%.

My coworkers were saying how they had 100s of knobs they could turn and try to try and tune the GC to either reduce the number of pauses, or do one big one every 10 seconds. . Java's options are more then I wish to list here. Go has `GOGC=` and that's it.

As one to one performance, that would be hard to measure, but my guess is Java would win in the end.


I don't agree with your pause time estimates. Most collectors are fast with small amounts of garbage in a small application. It's high allocation rates where GC time becomes a problem, and I don't see anything about Go or V8 designs to prevent very bad worst-case pause times.

Uh, most of the rant about Go GC below is out of date, they made some huge improvements from 2016-now. I'm leaving it up because someone replied to it

Unless something has changed in the last year or two, Go's GC is similar to Java's old CMS collector which is being deprecated.

Go's GC is non generational and non compacting, both hallmarks of modern GC algorithms. It's not a modern "moving" collector. It also has several stop the world phases. It's basically a design from the 70's. The GC uses old simple algorithms because the team was under a time crunch when it was developed. This may have changed, but that's how it was in 2018-2019 timeframe.

The pause times are short because GC runs very frequently. It has to because performance with large amounts of garbage is quite bad. This results in significant GC CPU overhead.

I don't know much about V8 collector besides that it is generational and compacting, so more modern than Go. But it's still a "stop the world" design. In that regard it's still closest to Javas old CMS collector.

Javas new collectors, ZGC and Shenandoah, both have near constant time stop the world phases. You can collect terabytes of garbage with only a few milliseconds pause. In V8 or Go, this would be many seconds pause time as the mark phase is "stop the world" in both.

You can find benchmarks that show one way or the other, but in badly behaving or allocation heavy applications, Java's new collectors or older G1 will perform far better. V8 and Go are dishonest about their GC performance by showing average pause times with high collection frequency. The important GC cycles are the long ones, so you really want to measure worse case pause time under load.

Under heavy load Go's design falls over. It's not compacting, not generational, and the mark phase pauses the application. IMO, it's just not a good GC. V8 is better, it is generational and compacting, but mark phase is still STW. Java's ZGC isn't generational but importantly, the mark and sweep phases don't stop execution. No matter how big your heap is and how much garbage, your GC pauses will be short


Go's garbage collection is optimised for reducing GC pause times, and while it is extremely fast in this regard this is only one of many ways GC performance can be measured.

If for your workload GC pauses are tolerable and throughput is important (say when doing offline CPU bound batch processing) then the Go GC is not optimal for your use case and will be slower than other options on the market.

Some runtimes (such as the JVM) allow the user to pick from one of many GCs so that they can use the one that is most appropriate for their workload.


I don't know this field, perhaps my bar is low because of Python lol, but Go's GC has very short GC pauses. So short that it makes non-GC'd usecases less attractive imo.

My big issue with Go's GC, or GC's in general, is consistency. Go's GC can still be variable in pause time iirc. But it's been ~3 years since i've worked in it - so maybe my memory is wrong :)


There is always a tradeoff in throughput (and a commercial low-latency GC has been available for Java for years, as well as realtime GCs). All I'm saying is that the reason Go is able to achieve low latency with a relatively simple design is because the language is designed to generate less garbage, so the challenge is smaller. My point is that Go's GC is not some extraordinary breakthrough in GC design (not that Hudson hadn't made some in the past) that unlocks the secret to low-pause GCs, but more of an indirect exploitation of the fact that the allocation rate is relatively low. The same design with much higher allocation rates would likely suffer an unacceptable hit to throughput.

A recent presentation on ZGC is here: https://www.youtube.com/watch?v=tShc0dyFtgw


It's not particularly fast, for instance. The golang GC has pretty short pause times but its actual throughput in GB/s collected does not stand out. Short pause times are nice for interactive systems but don't matter as much for eg batch or background jobs, where total time to completion is much more important. The sister comment also has a link to a GH issue detailing many more specific flaws of the GC.

But if I'm reading it correctly GO GC will actually rely on a using a lot of memory to ensure it can hit the pause time goal.

As far as I understand copying/generational collector by themselves don't add a lot of memory overhead. They simply add more complexity. You tend to require a lot more memory to ensure most your allocation & allocation happens in nursery (which can be scary fast and low overhead) and to allow the old generation GC to pace itself.

I wonder how much of the 20% expected GC overhead comes from the fact that they can't do the nursery GC optimisation and whats the expected overhead when it goes generational in 1.6

One thing I absolutely hate with CMS is the fallback single thread collector for Old Gen that makes for a scary possibility. Don't think GO will have one of those which is nice to see (not for me though :().


I think the Go GC guarantees pauses bounded below 100 microseconds, regardless of the heap size. Of course, that means incremental GC.

GC pauses can be hundreds of milliseconds. You could perhaps use a particular GC scheme that guarantees you never have more than a couple millisecond pause, but then you have lots of pauses. That might have unintended consequences as well. I'm also not sure that such GCs, like golangs, can really mathematically guarantee a minimum pause time.
next

Legal | privacy