-
-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blog Post: Understanding Garbage Collector Performance #4244
Conversation
```bash | ||
> javac GC.java | ||
> java -Xmx1g -XX:+UseParallelGC GC 500000 200 | ||
> java -Xmx1g -XX:+UseConcMarkSweepGC GC 500000 200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if having CMS still here in the example is that great. Its not shipped in newer JDKs.
So, maybe other options like the low latency collector is better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to have ZGC generational too.
## Conclusion | ||
|
||
Garbage collectors can be arbitrarily complicated, with the GCs differing in design | ||
and implementation between langauges (Python, Java, Go, etc.) and even within the same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and implementation between langauges (Python, Java, Go, etc.) and even within the same | |
and implementation between languages (Python, Java, Go, etc.) and even within the same |
The simplest kind of garbage collector splits the 16-slot heap we saw earlier is split into | ||
two 8-slot halves. If |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplest kind of garbage collector splits the 16-slot heap we saw earlier into
two 8-slot halves.
|
||
To do a garbage collection, the simplest garbage collector first starts from all non-heap | ||
references (e.g. the `STACK` references above) often called "heap roots". It then traces | ||
the graph of references, highlighted red below: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the graph of references, highlighted [in] red below:
randomly pauses for 1-2 seconds while they're playing it. | ||
|
||
Even from the limited description above, we can already make some interesting inferences | ||
about how the performance of a simply garbage collector will be like. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simply => simple
numbers, there are some interesting conclusions that can be drawn: | ||
|
||
1. *Caching data _in-process_ makes garbage collection pause times _worse_!* If your | ||
program in CPU-bottlenecked then caching to save computation can be worthwhile, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
program [is] CPU-bottlenecked then caching to save computation can be worthwhile,
|
||
|
||
:author: Li Haoyi | ||
:revdate: 3 January 2024 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2025
blog/modules/ROOT/static/GC-bench.sc
Outdated
else { | ||
println(s"Benchmarking liveSet=$liveSet heapSize=$heapSize") | ||
val javaBin = os.Path(sys.env("JAVA_HOME")) / "bin"/"java" | ||
val res = os.proc(javaBin, s"-Xmx${heapSize}m", "-XX:+UseZGC", "GC.java", liveSet, 10000, 5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val res = os.proc(javaBin, s"-Xmx${heapSize}m", "-XX:+UseZGC", "GC.java", liveSet, 10000, 5) | |
val res = os.proc(javaBin, s"-Xmx${heapSize}m", "-XX:+UseZGC", "-XX:+ZGenerational", "GC.java", liveSet, 10000, 5) |
Isn't it better to see how the Generational mode of ZGC behaves?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of Java 23 Generational mode is the default and non-generational mode is deprecated JEP 474: ZGC: Generational Mode by Default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. So my point was we should rather run that, non-generational ZGC is irrelevant at this point.
Is that the case? Are we running the benchmark against OpenJDK >= 23? If yes, than that's great, I apologize for my misunderstanding. I thought it's being run against OpenJDK 21, because the article links to 21 docs https://docs.oracle.com/en/java/javase/21/gctuning/z-garbage-collector.html
{rank=same; heap1; heap2} | ||
heap2 [shape=record label="HALF2 | <f0> | <f1> | <f2> | <f3> | <f4> | <f5> | <f6> | <f7> "] | ||
|
||
heap1 [shape=record label="HALF1 | <f0> foo | <f1> bar | <f2> baz | <f3> qux | <f4> new1 | <f5> | <f6> | <f7> "] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
baz | qux
shouldn't this be <f2> qux | <f3> baz
?
No description provided.