Skip to content
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

Merged
merged 12 commits into from
Jan 10, 2025

Conversation

lihaoyi
Copy link
Member

@lihaoyi lihaoyi commented Jan 2, 2025

No description provided.

```bash
> javac GC.java
> java -Xmx1g -XX:+UseParallelGC GC 500000 200
> java -Xmx1g -XX:+UseConcMarkSweepGC GC 500000 200
Copy link
Contributor

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?

Copy link
Contributor

@He-Pin He-Pin Jan 5, 2025

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines 92 to 93
The simplest kind of garbage collector splits the 16-slot heap we saw earlier is split into
two 8-slot halves. If
Copy link
Contributor

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:
Copy link
Contributor

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.
Copy link
Contributor

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,
Copy link
Contributor

@ayewo ayewo Jan 3, 2025

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2025

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Member Author

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

Copy link
Contributor

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

@lihaoyi lihaoyi marked this pull request as ready for review January 10, 2025 07:16
@lihaoyi lihaoyi merged commit d58dabc into com-lihaoyi:main Jan 10, 2025
28 checks passed
@lefou lefou added this to the 0.12.6 milestone Jan 10, 2025
{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> "]

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants