Live data from Hacker News

Go does not need a Java-style GC

erik-engheim.medium.com

101–110 of 226 posts

Re: Go does not need a Java-style GC

#101
post #10

Cool article, I'm not sure I agree with the headline. I used to write low-scale Java apps, and now I write memory intensive Go apps. I've often wondered what would happen if Go did have a JVM style GC. It's relatively common in Go to resort to idioms that let you avoid hitting the GC. Some things that come to mind: * all the tricks you can do with a slice that have two slice headers pointing to the same block of memo…

> Both are technically possible in Java, but I've never seen them used commonly (though in fairness I've never written performance critical Java.) I don't know about the Java world, but in C#—especially in games written in Unity—object pooling is very common.

I've seen it on a large site written in c#. Object pools of stream objects for serializing and deserializing data. This was 10 years ago.

Re: Go does not need a Java-style GC

#102

This is mostly stupid. Being able to have many GCs is a good thing. The big reason for "value types" is controlling spacial locality in memory, not GCs being bad per-se. Also, they undersell the java/c# situation. C# has "ref, out, or in", but even without those, you can always make a reference wrapper that has the value type as a field. So "reference types suck because coppying" is nonsense garbage.

> The big reason for "value types" is controlling spacial locality in memory, not GCs being bad per-se.

Spacial locality is one reason. Another very important reason is not generating unnecessary garbage. If you have a lot of microallocations, then your program is going to be slow regardless of you using manual memory management, RAII or a tracing GC. There is just a lot more stuff to do. With manual memory management, you're just going to call free() a lot (using big forward linked lists and freeing them with a function iterating over them, calling free() on each node? yikes). With RAII you're going to spend a lot of time in destructors due to an avalanche of objects freeing the objects they own (structs holding unique_ptr/shared_ptr to structs holding unique_ptr/shared_ptr to...? std::vector/std::vector? these are all antipatterns when it comes to performance). With GC, you're going to have long GC pauses, because the GC needs to traverse a bigger graph of pointers (deep/broad trees of heap allocated objects? arrays of boxed objects? yikes).

Another reason is eliminating unnecessary pointer indirection, which helps make sure that instead of loading data with two MOVs from memory polluting the cache, you need only one.

For my current project, I use arenas to opt-out of RAII and have just a couple points in my project where memory is freed, instantly in one go. Needless to say, I don't use arrays of boxed elements. It's working great for me. Not only is it faster, but also simpler to understand and I don't need to fight with the borrow-checker as much as in normal (RAII) Rust, because my objects are grouped into longer lifetimes, so I don't need to think about separate lifetimes for every distinct little object.

That said, I'm spending some time gathering ideas for a toy language I'd design for myself to write an OS from the kernel up to userspace in, and I'm leaning towards a GC-based design that relies a lot on value types. One way or another, allocating a lot of little objects is stupid, because in every scenario it makes your program slower and, code quality-wise, at best it doesn't make a difference, but a lot of times it makes your code harder to reason about, because now a lot of things are remote to the place in code you're thinking about at any given time.

Re: Go does not need a Java-style GC

#103
Skimmed this and found that a lot of claims are inaccurate and don't have data... Then realized, ugh, it's another Medium post. One particular wrong claim:

> In C#, for example, reference objects and value objects live entirely separate lives. Value objects are always passed by value. You cannot take the the address of a value object and pass that around. No, you need to copy the whole object every time.

In C#, there are normal, C-style pointers. (Although they aren't used very often, mostly when interoperating with C APIs.) Furthermore, you can pass a struct by reference if copying the struct is a problem.

I once wrote some performance critical code where I used structs and past them around by pointers. I ran the code through a profiler, and then refactored to use normal idiomatic non-pointer code. After running through a profiler there was no performance difference.

---

The author of this post really needs to base their claims on actual data. Some well-tuned programs in the languages compared and then compare the performance.

Re: Go does not need a Java-style GC

#104

Earlier quoted context omitted.

I would never write something like this in java, but to be fair, a program shouldn't be written like this in the first place. If you "need" a billion strings in memory and you didn't design for that with something that would scale better, you messed up a long time ago.

Huh, I didn't really have a problem solving the problem as it came up. Like many scaling problems, it wasn't a problem until it was. Then I fixed it. Now I have a solution that can deal with ten times as many strings as before. If I grow out of that one, I'll come up with a better design. I could have gotten 10 times as much hardware instead, but that would be an incredible waste of money compared to just spending a…

I have addressed similar problems using a typed array of contiguous memory and another array of lengths.

Re: Go does not need a Java-style GC

#105
post #12

There are very few falsifiable statements in this article but the extant ones are all demonstrably false, starting with this whopper: "This typically causes Java programs to have complete freezes of several hundred milliseconds where objects get moved around" Yeah, i mean, definitely not. The only language I regularly work with that has this property is, surprise, Go. The proof is in the tasting, as they say. Go look…

gRPC Java wins in part due to not allocating memory when reading and writing buffers. It uses Java-based implementation of jemalloc (via Netty) to manage its own memory. No/low allocations means the GC doesn't need to wake up so much.

Re: Go does not need a Java-style GC

#106

Skimmed this and found that a lot of claims are inaccurate and don't have data... Then realized, ugh, it's another Medium post. One particular wrong claim: > In C#, for example, reference objects and value objects live entirely separate lives. Value objects are always passed by value. You cannot take the the address of a value object and pass that around. No, you need to copy the whole object every time. In C#, there…

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.

Re: Go does not need a Java-style GC

#107
post #71
post #43

Earlier quoted context omitted.

The go gc was greatly improved in the versions subsequent to the ones used by Discord. The timing there was unfortunate.

Besides sometimes engineers just want to use Rust.

Ah yes! A new shinny thing. I've had plenty of conversations where some 'new' framework / language solves a 'huge and unsolvable' problem (but that has not actually been deeply investigated), or is just 'cool because XYZ company are using it' ... ¯\_(ツ)_/¯

Re: Go does not need a Java-style GC

#108
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…

I agree. The author seems to know quite a bit about Go and GCs, but doesn't seem to have much experience with Java. As a Java performance engineer, it sounds like he is comparing Go to how he thinks Java works based on what he's read about it.

[deleted]

Re: Go does not need a Java-style GC

#109
Did Go ever fix that problem where short term high memory usage can cause large empty heaps because of the lack of compaction?

I remember a rash of articles about this phenomena a while back. This article seems to think its not an issue so I'm wondering when it was fixed.

Re: Go does not need a Java-style GC

#110
post #100

Earlier quoted context omitted.

> Both are technically possible in Java, but I've never seen them used commonly (though in fairness I've never written performance critical Java.) I don't know about the Java world, but in C#—especially in games written in Unity—object pooling is very common.

Writing High Performance .NET Code ( https://www.writinghighperf.net/ ) has a chapter on this. In C#, time spent collecting depends on the number of still-living objects. That means you want objects you allocate to be short-lived (dead by the time GC happens) or to live forever (they go to the gen 2 heap and stay there). The book suggests object pooling when the lifetime of objects is between those two extremes, or w…

Ideally you would pool anything you might need to dynamically allocate during a level. You want to avoid allocations during game play entirely, if possible.

Unity itself will pool most media assets. Any given texture asset is shared between all object instances that use that texture. The programmer will end up pooling instances of their objects or just use structs and such. It can be tedious but I wouldn't call it more clunky than explicit memory management.

Large collections are actually not a problem at all in games as long as you only run the collection during a load screen.

Post reply on HN