Live data from Hacker News

Go does not need a Java-style GC

erik-engheim.medium.com

161–170 of 226 posts

Re: Go does not need a Java-style GC

#161
post #113

Earlier quoted context omitted.

This is a bit of a tangent, but you can get into situations where Java's memory-overhead becomes pretty untenable. I was in a situation of having to keep track of ~1 billion short strings of a median length of maybe 7 characters. In terms of just data, that should clock in at about 10 Gb; in practice it was closer to 24 Gb. I tried going with just byte[]-instances instead, which didn't help a lot. Using long byte[]-i…

A factor of 2.4 is extremely unlikely to be what makes the difference between a program being viable and not. You have to be in an extremely fine-tuned situation for that to make the difference, and unless your usage level is unusually static it's probably only going to make the difference for a few weeks or months.

So for some context, I'm running a search engine on consumer hardware. Coaxing power out of limited hardware is sort of my bread and butter. How many of these strings I can keep in memory limits how many documents I can index. Doubling that number is quite significant.

This number is orthogonal to how many searches I can serve, that is already a solved problem (through similar craftiness). Multiple searches per second sustained load is fine. The search engine has held up to the hacker news front page, which is something many supposedly non-interactive sites struggle with.

This is a non-profit operation, so unless someone just decides to pay me a months salary or two, I'm not able to meaningfully increase the hardware. I'm stuck with what I've got and will make the most of it.

Re: Go does not need a Java-style GC

#162
post #6

> In a multithreaded program, a bump allocator requires locks. That kills their performance advantage. Java uses per-thread pointer bump allocators[1] > While Java does it as well, it doesn’t utilize this info to put objects on the stack. Correct, but it does scalar replacement[2] which puts them in registers instead > Why can Go run its GC concurrently and not Java? Because Go does not fix any pointers or move any o…

Scalar replacements (as currently implemented in Java) does not work in real-world programs. Well-written code does not need it. Poorly written code can not trigger it, because the JIT is too dumb and isn't getting better. There is no sane test to determine whether a piece of code will be inlined in Java. In practice anything more complex than byte array is unlikely to be inlined. Even built-in ByteBuffers aren't! Me…

The JIT is getting better. Major escape analysis upgrades are a big part of where Graal (a drop-in replacement for the HotSpot JIT) gets its performance boosts. EA definitely does work well there because Truffle depends on escape analysis and scalar replacement very heavily. GraalVM CE is better than regular HotSpot at doing it and GraalVM EE is even better again.

Re: Go does not need a Java-style GC

#163
post #116

Earlier quoted context omitted.

> It feels like a huge trade-off of GCs is almost completely gone. FWIW the tradeoff of low latency GC is usually paid in throughput. That is definitely the case for Go, which can lag very much behind allocations (so if your allocation pattern is bad enough the heap will keep growing despite the live heap being stable, because the GC is unable to clear the dead heap fast enough for the new allocations).

throughput can be fixed by adding compute. latency cannot. always optimize for latency with gc. and no the heap will not keep growing in golang. it'll force threads to help with GC if its falling behind. thereby reducing the rate of allocations and speeding up the collection.

Only in some kinds of apps, like web servers where all the heavy lifting is being done by the database anyway.

Consider a compiler. It's not infinitely scalable to multiple cores. It may not even be multi-threaded at all. It also doesn't care about pause times - for that you want Parallel GC.

Re: Go does not need a Java-style GC

#164

Earlier quoted context omitted.

It's a lot easier to build custom allocators in C++ though. For one, Java has a maximum mmap-size of 2 Gb, and as a cherry on top of that turd, you have no control over their lifecycle. The language is very clearly not designed for this type of work, and if you try to make it do it anyway, it fights you every step of the way.

The foreign memory API which is currently incubating should help with most of these limitations: https://openjdk.java.net/jeps/419

Right - specifically they invented a way to make closing an mmapped segment safe and fast. The reason you can't officially (without private APIs) unmap something in current Java is because if you did then other threads would segfault, and "no segfaults" is kind a defining characteristic of Java. The new API fixes this using more VM magic, so closing a mapping from one thread will cause other threads to throw exceptions, but this is done without sacrificing performance (it doesn't require checking a status every time you read from memory).

Re: Go does not need a Java-style GC

#165
post #130
post #127

Earlier quoted context omitted.

It's odd how most people that haven't used a VM with GC are amazed by Go (no VM) and WASM (no GC) but still fail to understand that with a GC _AND_ VM you can code something that doesn't crash even if you make a big mistake! And they haven't even bothered to try it out! To use anything other than JavaSE/C# on the server you really need very good arguments!

Or being amazed by Go's compile speed, when Turbo Pascal and Object Pascal compilers were already doing that in the 1980's, or finding WASM innovative when polyglot bytecodes with no GC also go back to the early 1980's, like Amsterdam Compiler Kit EM as one example among many.

I just wrote some Go last weekend and the compile time was very slow. It reminded me of Scala. Any way I switched to Ruby and didn't have to deal with it any more. Turbo Pascal really was fast, but I don't see that in Go.

Re: Go does not need a Java-style GC

#166
post #142

The author doesn't really understand how Java escape analysis works, and just focuses on one key aspect: "It does not replace a heap allocation with a stack allocation for objects that do not globally escape." The author then implies that escape analysis is only used to reduce lock acquisition. Java escape analysis will replace a heap allocation with a stack allocation if the code is fully inlined. This is known as s…

I don't know much about this, but upthread various people say that scalar replacement happens very rarely if at all, currently. E.g.: https://news.ycombinator.com/item?id=29324132 . Could you perhaps comment on that, since you seem to have experience?

The issue is not that it doesn't happen - it does, all the time. The problem is it's unpredictable, hard to control and hard to measure. So it's sort of magic that gets blurred into all the other optimizations the VM is doing, and refactoring your code can make the difference between it happening or not.

Re: Go does not need a Java-style GC

#167
post #141

Earlier quoted context omitted.

Pooling objects (for the purposes of minimizing GC) is consider a bad practice in modern Java. The article suggests that compacting, generational collectors are a bad thing, but they can dramatically speed up the amount of time it takes to deallocate memory if most of your objects in a given region of memory are now dead. All you have to do is remove objects that are still alive, and you're done: that region is now a…

Does object pooling still make sense for direct ByteBuffers nowadays?

Yes. Those aren't GC controlled so any arguments about GC is irrelevant with direct byte buffers.

Also, object pooling isn't really a GC related hack, it's more useful as a cache booster. Programmers like immutability and garbage collection but your CPU doesn't like these things at all. If you're constantly allocating new objects it doesn't matter if your GC kicks ass, because those objects will always be in parts of memory that are cold in the cache. If you allocate some up front and mutate them, they're more likely to be warm.

Obviously this isn't a language or even VM thing. It's a "mutable variables are good for performance" thing.

Re: Go does not need a Java-style GC

#168
post #78

Earlier quoted context omitted.

Java has a pretty decent standard library with different list, map and set implementations and quite a few third party libraries with yet more data structures. Honestly, Go felt a bit primitive and verbose to me on that front on the few times I used it. Simplicity has a price and some limitations. There are also other tricks you can do like for example using off heap memory (e.g. Lucene does this), using array buffer…

Not sure if you're in on the joke, but for those who didn't go to the repo itself: https://github.com/golang-jvm/golang-jvm It's just a copy-paste of JRuby on April 1st and the readme now includes a rickroll. Maybe it's irresponsible of them to leave it up in a way that Google still finds as a legitimate-looking search result.

LOL, I was not aware and stepped right into that.

There appear to be other attempts: for example https://github.com/zxh0/jvm.go (might be the same?)

Let's just say people have tried/joked about it but it never took off.

Re: Go does not need a Java-style GC

#169

Earlier quoted context omitted.

In general when talking about quantitative subjects, we need to use quantitative measures. I think the author is nearly there, but in general, unless you have data to refute your claims, it should be ignored. I don't say this snarkely, but in a domain where we actually have hard quantitative measures, they should be used and required for argument.

Quantitative measures would be nice, but I would imagine that it's going to be really difficult to quantitatively compare go and java garbage collectors, without a billion other factors about the language/runtime getting in the way.

I doubt it'll be that hard. Just write the same memory intense routine in both languages, and time it running in a loop for a couple million executions.

Make sure the code is idiomatic and optimized.

Re: Go does not need a Java-style GC

#170
post #65

Earlier quoted context omitted.

"Scalar replacement" explodes the object into its class member variables and does not construct a class object at all. That does result in the exact same `sub %esp` (that Go would do for any struct), but it is restricted to only working if every single usage of that class type is fully inlined and the class is never passed anywhere that needs it in its object form. It's worse than what Go has. Go can stack-allocate a…

Scalar replacement does not work even in very trivial cases: https://pkolaczk.github.io/overhead-of-optional/ In all those cases, Optionals were inlined, didn't escape, yet they haven't been properly optimized out.

Did you understand why?
Post reply on HN