So this is an article in response to the following article.
https://www.unlogged.io/post/z-garbage-collector-in-java
Why I am writing this article?
The above article quite eloquently explains the ZGC+Generational and provides some basic comparisons in terms of GC events. I wondered since ZGC+Generational which is concurrent cleaning up the JVM will definitely need more CPU (processing threads) time than other GCs. It was not without experience, I have tried ZGC (non-generational) in Java 21 and it was not good to put it lightly.
So I was now wondering, should I checkout ZGC+Generational in Java 23?
Why the heck not? So here I am, after spending around 1 day in orchestrating a comparison test to be able to quantify the difference between G1GC, ZGC+Generational GC, Shenandoah GC using Java 23.
Where is the code?
I have used the code on the article provided above as base. The modified code can be found on the following git repo:
https://github.com/vishalendu/java-gc-demo
Note:It needs JDK-23, obviously 😁
The code does the following things:
Its a spring boot project with a rest interface to create objects on java heap:
For e.g. http POST call to /api/memory/load/500
will create
10MBX50 objects in the memory, they will get garbage collected soon
after.
I have added additional dependency to expose the metrics endpoint for prometheus to be scraped for metrics (CPU/GC etc).
Note: I tried to add code/dependencies to push metrics to InfluxDB, but faced some issues, so decided to go with the (pull based) Prometheus.
What is the setup like?
- Machine Used: AMD Ryzen 7 5800H with 8 cores, 30G RAM
- OS: Ubuntu 24.04
Docker containers were used to deploy Grafana/Prometheus.
-
JDK, tried:
- OpenJDK (build 23.0.1+11-39)
- Amazon Corretto OpenJDK (build 23.0.1+8-FR)
Note: Unfortunately Shenandoah refused to run with OpenJDK build 23.0.1+11-39, so had to repeat the whole experiment with the Amazon Corretto build 23.0.1+8-FR
Where can you find all the stuff?
JDK: You can google it! and download (depending on your platform!!)
Rest of the component, scripts, docker-compose etc. can be found on this repo:
https://github.com/vishalendu/java-gc-demo-supplement
README on the repo is self-explanatory, I assume you can run docker-compose and edit basic shell scripts before running. 😉
Results, finally !! 😍
TLDR: ZGC+Generational is in a league of its own.
Process CPU (P95)
Process CPU P95 is the least in ZGC+G
.
System CPU (P95)
System CPU P95 is the least in ZGC+G
.
Pause Time (ms)
Pause Time is the least in ZGC+G, which leads to better throughput, which also shows that ZGC+G does most of the collection concurrently without stopping JVM
.
GC Overhead
GC Overhead is the least in ZGC+G, which leads to better throughput
.
GC Concurrent Time
GC Concurrent Time is the highest in ZGC+G which shows that ZGC+G does most of the collection concurrently without stopping JVM
Note:
- You can checkout the values for all the test in the supplement repo provided in the excel sheet results-comparison.xlsx
- You can also checkout screenshots of the grafana dashboards for all the tests on the supplement git repo
Phew WOW!!
The ZGC+Generation GC Type in Java 23 looks revolutionary, I had my doubts, since its continuously and concurrently cleaning the heap, I was expecting more CPU Utilization. But I was completely amazed at the low CPU Utilization and the low (abismal) GC Overheads in the ZGC. Cannot wait to try it out in an actual application !!!
Shenandoah, personally I have not used it but it fared better than G1GC. Coming in the second place.
G1GC, it feels like the GC overheads and the high CPU utilization have costed G1GC dearly, its the worst performing of the three. Never thought I would be saying this but I did not expect this vast difference in performance.
Thanks
Please let me know what you feel about this article, anything more or anyother metrics that could be added to the comparison. 😎
Top comments (0)