Live data from Hacker News

Go does not need a Java-style GC

erik-engheim.medium.com

41–50 of 226 posts

Re: Go does not need a Java-style GC

#41

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.

I mean, I’ve dabbled in all of these languages, and I much prefer Go’s value types to ref, out, in, and a half dozen GCs. It’s nice that these VM languages have a distinct thing for every eventuality, but I much prefer a single thing that works 99% of the time.

Re: Go does not need a Java-style GC

#42
post #27
post #15

Earlier quoted context omitted.

The Java gRPC SDK is heavily optimized by Google, much more than the Go one. Until recently Java pauses were a real issue, I worked with a lot of Java based solutions ( elastic search, hadoop etc ... ) and was never impressed by the GC.

While I agree that the java gRPC lib is better maintained, I don’t agree with your second point. Java’s GCs are the state of the art, that can manage heap sizes up to 16 tera bytes. Other GC implementations would simply die there.

That’s neat, but a lot of applications will never need 16TB.

Re: Go does not need a Java-style GC

#43

I'm cautious about believing this sort of claim because I remember reading about why Go doesn't need generics, yet here we are waiting for Go Generics to be ready. However, the article convincingly explains who Java has a greater need for a compacting GC - it creates more garbage. This doesn't necessarily mean Go won't benefit from having a generational, compacting GC at some point, for some applications.

Even if it doesn't need specifically a compacting QC having a swappable more tunable one might still be a big deal for people who need something other than the current GC. Like a simple example is the Discord article about how their use case could not work with Go because it always ran the GC every two minutes. If they could optionally disable that feature they theoretically could have kept using Go instead of rewrit…

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

Re: Go does not need a Java-style GC

#44
post #29
post #22

Earlier quoted context omitted.

As I said the Java SDK is heavily optimized to do the least minimum of allocations. I mean if you don't allocate much the GC is not really an issue.

OK but again, that is a statement totally contrary to the article. The article is claiming two things: that it is easier in Go to avoid the heap while Java is utterly dependent on allocating everything on the heap - which is not consistent with the experience of actual Java and Go programmers - and that Java has a "preference for high throughput and high latency" which it doesn't.

FWIW, I used to write some high-performance JavaScript. One of the tricks of the trade was to allocate early and make sure you avoid allocating once the loading phase has started, hence avoiding triggering the garbage-collector (in most runtimes/languages, gc phases are typically triggered by the allocator). This involved writing very non-idiomatic code, to a large extent reimplementing a form of high-level custom allocator on top of the existing allocator/gc, but it worked.

I'd be very surprised if the same wasn't possible in Java.

I don't know if this is how gRPC is written but it could explain an apparent contradiction.

Re: Go does not need a Java-style GC

#45
post #38
post #37

Earlier quoted context omitted.

Max pause times of 0.5ms is what got me really interested. It feels like a huge trade-off of GCs is almost completely gone. https://malloc.se/blog/zgc-jdk16

There's still a memory tradeoff, some due to GC, some due to Java (lots of runtime reflection...). Guessing 2-4x.

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[]-instances and indexing those doesn't help as much as you'd think because they get sliced up into small objects behind the scenes.

I ended up memory mapping blocks of memory and basically implementing my own append-only allocator.

Re: Go does not need a Java-style GC

#46
> In a multithreaded program, a bump allocator requires locks. That kills their performance advantage.

Wait, what?

What's wrong with:

    char theHeap[0x1000000];
    atomic_ulong bumpPtr;

    void* bump_malloc(int size){
        uint32_t returnCandidate = bumpPtr.fetch_add(size, std::memory_order_relaxed); 
        if(returnCandidate + size >= HEAP_SIZE){
            // Garbage collect. Super complicated, lets ignore it lol.
            // Once garbage collect is done, try to malloc again. If fail then panic().
        }
        return &theHeap[returnCandidate];
    }
------

You don't even need acq_release consistency here, as far as I can tell. Even purely relaxed memory ordering seems to work, which means you definitely don't need locks or even memory barriers.

The exception is maybe the garbage-collect routine. Locking for garbage collect is probably reasonable (other programmers accept that garbage-collection is heavy and may incur a large running + synchronization costs), but keeping locks outside of the "hot path" is the goal.

------

This is what I do with my GPU test programs, where locking is a very, very bad idea (but possible to do). Even atomics are kinda-bad in the GPU world but relaxed-atomics are quite fast.

-------

> In Java, this requires 15 000 separate allocations, each producing a separate reference that must be managed.

Wait, what?

    points [] array = new points[15000];
This is a singular alloc in Java. 15000 _constructors_ will be called IIRC (My Java is rusty though, someone double-check me on that), but that's not the same as 15000 allocs being called, not by a long shot.

------

Frankly, it seems like this poster is reasonably good at understanding Go performance, but doesn't seem to know much about Java performance or implementations.

Re: Go does not need a Java-style GC

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

To be fair, early Java definitely had lots of issues with GC and "hanging" programs due to GC activity. It's a failure of the article to bring it up if modern Java implementation are better on that point. As an example of bad reputation, I think the latest Eclipse IDE on the latest Java SDK is still sucky and because of my ignorance I blame it on Java.

I was disappointed with the first Go gRPC implementation, it allocated like crazy and was really slow because of it. It's been rewritten since then but I don't know how much better it is now. To get good performance out of Go it is important to think about memory allocations (unfortunately), just as it is in C and C++. Although allocating like crazy in C/C++ will probably mostly result in a general loss of performance, not affect tail latencies in network protocols much.

Re: Go does not need a Java-style GC

#48
> Java is a language that basically outsourced memory management entirely to its garbage collector. This turned out to be a big mistake.

Given that Java is not far behind C and C++ for many kinds of workload and in quite some scenarios can outperform C code, I'm not sure that I buy this line of reasoning.

> Doing these updates requires freezing all threads.

Eh, what? There are multi-threaded GCs, what is this article even talking about?

> However, this does not put C# and Java on equal footing with languages like Go and C/C++ in terms of memory management flexibility

In which world are Go and C in the same worlds when it comes to memory management flexibility? That's like comparing a language that uses a Garbage Collector to one that requires manual memory management. Because - it is.

> Modern Languages Don’t Need Compacting GCs

> If need be, the Pacer slows down allocation while speeding up marking.

So, you just traded in one drawback for another?

Heck, I get it. The JVM is not a thin graceful fawn. It's a complicated beast that requires years of experience to tame it - and even then it will come back from time to time an bite you. There are many good points to critique the JVM - but I don't get the feeling that the author of this article has spent much time with modern JVMs because he's not pointing out any of them.

Post reply on HN