News for hackers. Stuff that matters.
Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Sunday September 20, 2020 @05:24PM (#24537548)
That's not always true. Most of the time when Java uses a ton of memory it's because people use the default memory settings. If you tell Java to use up to 90% of system RAM, it will. Garbage collection is expensive so it will delay until memory is depleted.

This is a different GC design than V8 and Go, which use older collector designs with high overhead. They need to collect very frequently because their stop the world pauses get longer with more garbage. Javas new collectors are near constant time, even with terabytes of garbage, so it's much more efficient to wait until the heap builds up and collect much less frequently. Ironically, Java appears to use tons of ram because it has better garbage collectors.

When you configure Java GC to collect frequently, it turns out Java uses 2-3X less ram than JS, and far less than Python, Ruby, etc. It does use more than Go, about 2X. But the point is it uses a lot less ram than most other popular languages for web dev.

Unfortunately the "Java uses too much ram" is used in defence of using things like JS and interpreted languages, when in actually it uses much less if you configure it to.

Re:Java Turns 25 – Whats Next? [pdf] by zmmmmm on Sunday September 20, 2020 @10:06PM (#24539083)
Not sure that is all of it. You can try turning down the max total memory to eg: 64mb and see what you can still run. An awful lot of stuff just can't, while similar applications in conceptually higher level languages (eg: Python, PHP, etc) happily execute.

The various factors I see are:

    - JVM overhead 10-15mb seems to be required just to get off the ground,
      which directly relates to the JVM replicating a bunch of OS 
      functionality (inevitable tradeoff for portability)
    - Missing value types - any complex data structure ends up with 
      big overhead from storing references
    - Stack frames - if you need a lot of threads you need a lot of stack,
      and a surprising number of threads run
Some of this will get addressed in upcoming releases, will be interesting to see how it goes.
Re:Java Turns 25 – Whats Next? [pdf] by pkolaczk on Monday September 21, 2020 @02:35AM (#24540475)
GC and memory settings are not the only cause. There are several other factors:

* JVM overhead - all these advanced optimizing compilers and GCs have non-zero footprint. They need RAM to run their code and they need RAM to perform their tasks.

* Compiled code cache - JVM keeps both the original bytecode and generated machine code in RAM.

* OOP overhead - each object has 2 or 3 words of overhead for the object header vs zero in languages like C or Rust. Even when you don't need dynamic dispatch or object locking, you pay for it.

* Inability to compose bigger structures other than by allocating separate objects and using pointers to reference them - these pointers need space and are not cheap on 64-bit architectures. This is going to probably partially improve with Valhalla, but at this point it is mostly guessing and it has been in development for years.

* No support for packed arrays.

* The smallest unit of loadable code is a class. If you needed a single function, the JVM loads a whole class containing it, and its required dependencies. This is not only bad for memory usage but also for startup time. Unless you pay a lot of attention, it is easy to load 80% of code in order to just display a help message (this is based on a real issue I worked on - I'm not making this up). Compare that to code in languages like C - the OS loads code that gets executed.

And back to GC - I agree the default settings are often to blame, but there is a reason JVM defaults to using RAM so aggressively. GC becomes very inefficient when it doesn't have enough "room". And low pause GCs achieve their low pause goals by trading throughput. Switch from parallel STW GC to G1 and your maximum sustainable allocation rate goes down by a few times.

Re:Java Turns 25 – Whats Next? [pdf] by rcoveson on Monday September 21, 2020 @02:36AM (#24540476)
When I hear "Java uses too much ram," lazy reclamation of the heap and other GC overhead are not the first things that come to my mind, personally.

Java doesn't have a "struct". If you want to represent an array of 64-bit signed integers, Java has you covered with its primitive arrays. But if you want to represent an array of anything more interesting than that, (say, a tuple of a double and a long), you have to serialize and deserialize those objects to and from parallel primitive arrays or byte buffers. Because if you do the language-natural thing and use an Object array, you're paying a huge price in memory: 4 or 8 bytes per pointer in that array, plus a 16 byte Object header on each Object. And, of course, those Objects are all individual allocations, not necessarily contiguous. That's a lot of overhead!

Of course, Java programmers concerned with memory usage don't put up with this. Lots of solutions have been devised. OpenHFT's Chronicle Values[0] is one example I came across recently. But this feels like fighting with the language compared to how easy it is to be efficient with memory in C. If you told a beginner C programmer to make an array of compound objects, it's not unlikely that their array will take up exactly as much space in memory as it intuitively seems like it should. (8 byte double + 8 byte long) * 100 values = 1600 bytes in C, no fuss. If you asked the average Java programmer for that they'd give you something that would take up 3 times as much memory. And because Java makes that behavior natural, it "uses too much ram". It doesn't matter as much that it's possible to convince it not too.

0. https://github.com/OpenHFT/Chronicle-Values

Re:Java Turns 25 – Whats Next? [pdf] by imtringued on Monday September 21, 2020 @04:58AM (#24541219)
What you are saying is true but even when you use optimized frameworks like Micronaut and stay below e.g. 50MB java heap usage you still end up using more RAM than what the Java heap says. Even the most optimized Java program on the planet will be beaten by a carelessly written C++ program. At least in terms of memory usage. I know it because I once wrote a React app with a C++ backend that also included sqlite. The C++ version needed 4MB of RAM at worst. Meanwhile the Java version didn't even include a database!
Re:Java Turns 25 – Whats Next? [pdf] by pjmlp on Monday September 21, 2020 @03:13AM (#24540672)
Because fighting the language is worth it given the benefits that come with it.

Plus many people overlook just writing a couple of native methods and be done with it.

Somehow this is how people write "Python".

Also Valhalla and Panama are around the corner.

Re:Java Turns 25 – Whats Next? [pdf] by titzer on Sunday September 20, 2020 @10:23PM (#24539172)
> This is a different GC design than V8 and Go, which use older collector designs with high overhead.

I think this information is a bit dated. Go has a highly advanced concurrent collector with very low pause times (~1-100μs). V8 also has incremental marking, concurrent marking, and parallel compaction. Its pause times are more like 100-1000μs. V8's GC has been tuned more and more to save memory (i.e. smaller heaps) because people have so many tabs open these days.

Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Monday September 21, 2020 @12:25AM (#24539786)
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

Re:Java Turns 25 – Whats Next? [pdf] by titzer on Monday September 21, 2020 @12:35AM (#24539835)
> 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.

> ... In V8 or Go, this would be many seconds pause time as the mark phase is "stop the world" in both.

Like I said before, your information is outdated. V8 has both incremental and concurrent marking. I even mentioned it in my comment, but apparently you didn't read that either. V8 only stops the world for semispace evacuation and compaction. It doesn't compact the entire old generation at once, but decides on a per-page basis.

For Go's GC, I am going by public information presented by one of its primary designers, Rick Hudson, who has since retired.

You can argue with his slides if you want. https://blog.golang.org/ismmkeynote

Java's new GCs sound fantastic! It's great for the field in general. However, I would encourage you to spend less time misrepresenting other people's work and making up numbers.

Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Monday September 21, 2020 @01:16AM (#24540054)
V8 still uses STW for mark sweep. The benchmark image on this https://v8.dev/blog/concurrent-marking shows 50+ms pause time, quite bad compared to ~5ms or less in new Java collectors. This might be due to STW move phase though? They don't really explain, but the long pause times show that there's definitely still long STW pauses in V8.

For Go, I'm going to be a bad HN user and not read the whole article. Sorry, it's just too long for this time of night. It does appear that my understanding of Go GC is out of date. There's been many improvements in the last couple years. Some strange behavior due to not enough knobs to configure GC, but it appears to have a near constant GC pause? https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i...

I'm annoyed that Google doesn't offer much benchmarking results for V8. Huge articles about improvements made with a single benchmark image. And they didn't use standard benchmarks for either so it's unclear what they're even benchmarking. The Go slides you linked include benchmarks from some guys production server he tweeted images of, a bunch of standard benchmarks but they only show % throughout improvement, and no pause times.

Re:Java Turns 25 – Whats Next? [pdf] by titzer on Monday September 21, 2020 @01:41AM (#24540182)
> V8 still uses STW for mark sweep.

Well unfortunately you are still misunderstanding, so let me be more precise so we are talking about the same thing. V8 uses incremental marking (i.e. splitting mark work into smaller chunks and interleaving those chunks with mutator time) as well as concurrent marking (i.e. multiple parallel collector threads marking in the background, concurrent with the mutator). Not mentioned in the article, but sweeping of pages is also incremental (i.e. dead space reclaimed on-demand when free lists run empty) and concurrent when idle (i.e. in the background). So the statement "V8 still uses STW for mark sweep" is just wrong. Like I said before, V8 only stops the world for semispace scavenges (fast, < 1ms) and compaction (slow, ??ms), but compaction is less frequent than mark/sweep, which is incremental and concurrent.

You also misunderstood what is reported here. That 50ms main thread marking time is cumulative, meaning those 50ms are spread over the entire garbage collection cycle, split up into small increments so that the mutator (main thread) is not stopped the entire time. It's explained there in the text and illustrated in the second-to-last diagram.

> quite bad compared to ~5ms or less

Again, it is not 50ms pause, it's 50ms work, split into much, much smaller incremental pauses, typically less than 1 ms each. That number is not presented in your linked article but is pretty typical. The V8 GC needs sub-millisecond pause times because it has a soft realtime requirement in that it may end up on the critical path for frame rendering (60fps = 16.6ms).

> For Go, I'm going to be a bad HN user and not read the whole article.

FTA "...The August 2017 release saw little improvement. We know what is causing the remaining pauses. The SLO whisper number here is around 100-200 microseconds and we will push towards that. If you see anything over a couple hundred microseconds then we really want to talk to you and figure out whether it fits into the stuff we know about or whether it is something new we haven't looked into. In any case there seems to be little call for lower latency. It is important to note these latency levels can happen for a wide variety of non-GC reasons..."

TLDR: if you see pause times of more than a couple hundred microseconds, call the red phone.

Also, please note, I am just trying to provide accurate information about the collectors I do know about, designed by people I work(ed) with. I don't know enough about ZGC or Shenenadoah to confidently assert anything about their performance characteristics, but based on what I read I am actually very excited to see them make it into production. I consider advances in GC to be overall a good thing for everyone, and would encourage you to be more open to learning the advantages and disadvantages of various systems without as much derision and not try to pick sides.

Re:Java Turns 25 – Whats Next? [pdf] by hilbertseries on Monday September 21, 2020 @01:42AM (#24540189)
Go also has simplified their heap from Java's in a notable way, you cannot set a limit to Go's heap. You can set a limit to the memory used by the Go process, of course.
Re:Java Turns 25 – Whats Next? [pdf] by philipkglass on Sunday September 20, 2020 @05:39PM (#24537630)
I'll shamefully admit that I have been running and writing JVM based services for years and I didn't know this. I thought that the fixed memory overhead for a simple JVM service was simply higher than with CPython as a fact of life.

There are times when I'd happily trade more frequent GC pauses for a smaller per-process memory footprint. How do you find a reasonably small Xmx that doesn't lead to OutOfMemoryError exceptions?

Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Sunday September 20, 2020 @06:13PM (#24537883)
> How do you find a reasonably small Xmx that doesn't lead to OutOfMemoryError exceptions?

That's tough to figure out. In new versions of Java, I think 14+, if you use ZGC collector it will return unused memory to the OS. Memory options vary depending on collector, but new versions of ZGC support "soft max" heap size and uncommit. Together it might be close to what you're looking for https://malloc.se/blog/zgc-softmaxheapsize

I should mention the GC situation was worse until the last few years. Until ZGC and Shenandoah came around, Java still didn't collect frequently but when it did there were long pauses. This is what V8 and Go's collectors were designed to avoid. They have more overhead from collecting frequently, but low pauses. With the new Java collectors you get the best of both worlds.

You can set heap ratios and such for older collectors to decrease Java memory use with those, but IMO you're better off using ZGC and uncommit these days

Re:Java Turns 25 – Whats Next? [pdf] by topspin on Sunday September 20, 2020 @09:32PM (#24538936)
> That's tough to figure out.

Indeed. That fact obviates this option in most cases. You have to spend time tweaking obscure, unstable knobs (the X in Xmx means Oracle is free to alter its meaning at any time) and risk either a.) serious failures in production or b.) poor results because the conservative choices necessary to avoid 'a' achieved little improvement and you wasted your time. The real world for most enterprises is a vast heard of communicating components and toying with GC switches multiplied by N things is a nonstarter.

So while you're technically correct that excessive memory use by conventional JVMs is "not always true," in practice you are wrong. That reality comes with a real cost that appears on a real bill every single month.

Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Sunday September 20, 2020 @11:52PM (#24539623)
The cost of setting a few command line args? They're not really obscure or unstable, just different depending on the GC you use. Turning on ZGC and setting the right options is like 4 command line flags. It's very easy, the reason it's not frequently done is that nobody reads documentation and it's not the default.

Go's approach of minimal knobs leads to unfixable problems in production. Java gives more options to tune GC for your use case

Re:Java Turns 25 – Whats Next? [pdf] by topspin on Monday September 21, 2020 @04:12AM (#24540961)
> Go's approach of minimal knobs leads to unfixable problems in production.

I didn't bring up Go, but since you did the thing I see is that Go -- a much younger language -- is going places Java never has, or did so only haltingly. Caddy is a case in point. Here is Go taking on nginx, haproxy, Envoy, etc.

The people that once imagined using Java for such things have retired or moved on to other battles. No one seriously ponders attempting 'systems' tasks with Java any longer; that whole space was ceded to more efficient languages. My opinion is that Java's poor efficiency -- a big part of which is its excessive memory consumption -- is the reason for this.

That's my opinion. What I know for fact is that today, when people are making design decisions about new services and their deployment, Java is a problem; it is understood that anything implemented in Java is going to sort right to the top of the list of memory pigs in the cluster, and you can only afford so many of those.

Re:Java Turns 25 – Whats Next? [pdf] by imtringued on Monday September 21, 2020 @05:11AM (#24541291)
I think you're ignoring that people don't want to "tune" their GC. They just want it to work. So instead, they are going with the obvious route of just buying more RAM. This is perfectly fine on servers, which is where Java shines. As soon as Java has to be used for e.g. CLI scripts, daemons, or situations in which multiple process instances have to run at the same time, then Java is an incredibly poor choice and there is nothing you can do about that. If you are a genius at memory optimization in Java then you'll see even bigger gains in C++ or Go or Lua or Javascript or Python. Some of these options may not be as fast as the JVM but this discussion is purely about memory usage.
Re:Java Turns 25 – Whats Next? [pdf] by hilbertseries on Monday September 21, 2020 @01:45AM (#24540201)
> I should mention the GC situation was worse until the last few years. Until ZGC and Shenandoah came around, Java still didn't collect frequently but when it did there were long pauses.

This feels like G1GC erasure. I'll also say we've tried out ZGC and while pause times were low, it had a huge CPU overhead and the performance of our application was notably worse and we went back to G1GC. We're still on Java 11, so maybe we'll see some magic when we eventually try the newer versions.

Re:Java Turns 25 – Whats Next? [pdf] by sideeffffect on Wednesday September 23, 2020 @03:43PM (#24571054)
20th October, OpenJDK 11.0.9 will be released, with full-fledged Shenandoah GC. It has _very_ low pauses and has the `-XX:ShenandoahGCHeuristics=compact` option, which will promptly give unused memory back to the OS (thus keeping the whole memory profile low).

Give it a try.

https://wiki.openjdk.java.net/display/shenandoah/Main

https://www.slideshare.net/jelastic/choosing-right-garbage-c...

https://twitter.com/shipilev/status/1308320432404168705

Re:Java Turns 25 – Whats Next? [pdf] by philipkglass on Sunday September 20, 2020 @07:18PM (#24538253)
Thanks for the advice. I'm stuck on 11 until the next LTS release but I'll definitely be trying ZGC next year. (ZGC is in 11 but still marked experimental, and I'm conservative about making changes.)
Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Monday September 21, 2020 @12:31AM (#24539813)
That's probably a good decision. When ZGC was first released it didn't support class unloading, including in 11. This will lead to puzzling memory leaks in applications that generate a lot of code at runtime.

Newer versions of ZGC support class unloading and have some other performance optimizations

Re:Java Turns 25 – Whats Next? [pdf] by sideeffffect on Wednesday September 23, 2020 @04:25PM (#24571574)
Shenandoah might help you. See my other comment for more details:

https://news.ycombinator.com/item?id=24571054

Re:Java Turns 25 – Whats Next? [pdf] by spullara on Sunday September 20, 2020 @06:01PM (#24537801)
Depends on your application and how much memory you actually need. I would also use ZGC as it returns unused memory to the system and low ms maximum pause times for terabytes of heap.
Re:Java Turns 25 – Whats Next? [pdf] by imtringued on Monday September 21, 2020 @04:51AM (#24541167)
I have never managed to get a JVM to use less than 100MB RAM. The application in question needed significantly less than 32MB of Java heap. Meanwhile equivalent lua programs can do just fine with 1MB RAM.
Re:Java Turns 25 – Whats Next? [pdf] by deepakarora3 on Sunday September 20, 2020 @07:44PM (#24538401)
All OK except when you need the same level of performance for those 0.001% of requests when the GC kicks in and takes the response time outside acceptable limits. Due to this reason alone, my company is planning to move off a popular Java based API gateway and to a C++ envoy side car implemented service mesh. And I am wondering if this is really worth it.
Re:Java Turns 25 – Whats Next? [pdf] by throwaway189262 on Monday September 21, 2020 @12:38AM (#24539858)
Consider trying ZGC first. The main selling point is low worst case pause time. I've seen some tests where P99.9 pause time was less than 5ms, vs several hundred for older collectors.

In my limited tests I never saw a GC pause over 5ms. I was basically hammering a Spring Boot application with HTTP load tester.

Re:Java Turns 25 – Whats Next? [pdf] by pkolaczk on Monday September 21, 2020 @02:48AM (#24540540)
GC doesn't need to pause to have a negative effect on application latency.

While not-pausing is generally a huge improvement (after several decades of GC development), now what about thrashing of CPU caches?

Re:Java Turns 25 – Whats Next? [pdf] by sideeffffect on Thursday September 24, 2020 @08:28AM (#24577921)
Shenandoah might help you. See my other comment for more details:

https://news.ycombinator.com/item?id=24571054

Re:Java Turns 25 – Whats Next? [pdf] by zmmmmm on Sunday September 20, 2020 @10:07PM (#24539087)
They should definitely investigate the new low-latency gc options in the JVM.