
JVM Anatomy Quarks - signa11
https://shipilev.net/jvm/anatomy-quarks/
======
jnordwick
I have been reading Aleksey Shipilev for a decade now, and his insight in high
performance Java was been a well worth the time. He is also the maintainer of
JMH, Java Microbenchmarking Harness, a tool I miss in about every other
language especially Go. Because writing microbenchmarks is really difficult
especially on a JIT that wants to rewritw and optimize things behind your back
a couple times. Hotspot can be both brilliant and maddening.

Thanks to much of his writings and the rest of the Java performance community
(pretty large in the fintech sector), I can write faster Java than most people
can with C++. It just takes some effort, but the control and performance you
can get from Java is really impressive.

I've had to deep dive into Go a little more lately, and I really miss some of
the Java support. I've found Go to be much slower when you have to do anything
interesting. In high performance Java you often rewrite a lot of the base
libraries in a very different style that gives you tight control over escape
analysis, GC, call site inlining, etc. You actually have a decent amount of
control for such a high-level language.

In Go, I haven't been able to find that control. The Go team seems to have
taken an opposite approach and removed your control (I often joke about Go
just being short for "Go Fuck Yourself" because of its attitude against
developer control and the teams's "if we don't need it you don't need it"
attitude).

It is resources like this that really make Java shine in its pro high
performance developer attitude. (Current Go issue, getting select and channels
to operate anywhere remotely efficiently and trying to find a way to keep high
CPU goroutines on different OS threads - so far not much luck).

~~~
closeparen
Benchmarking and profiling are both built into the language, which is pretty
nice. But you’re right, Go is designed for safety over control. High
performance Go comes down to avoiding allocations; there’s not too much else
you can do at the language level.

~~~
jnordwick
Not sure if you can even control GCs that well some times. In Java I've worked
on projects that never GC once they reach a steady state. I'm not sure I could
do the same in Go, or maybe the techniques are just more involved or
different?

~~~
closeparen
The technique is basically to preallocate your structs and reuse them via
*sync.Pool, never allocate on a hot path. I don't know about "never" GC but it
at least minimizes GC, and the pauses are very short.

------
ajkjk
This is good stuff. I wish it existed for a lot of other things -- like python
and v8. Or maybe it does? Where can I find deep technical knowledge in bite-
size form?

~~~
shortercode
While not quite the same the V8 team has a blog that discusses various
improvements they are making. Such as the Orinoco GC project. Which typically
contains some details on the implementation.

It's a fairly fascinating window into the inner workings, and quite detailed.

[https://v8.dev/blog](https://v8.dev/blog)

~~~
jashmatthews
The WebKit blog also has really good info on JSC E.g.
[https://webkit.org/blog/7122/introducing-riptide-webkits-
ret...](https://webkit.org/blog/7122/introducing-riptide-webkits-retreating-
wavefront-concurrent-garbage-collector/)

Filip Pizlo’s site has a bunch of his presentations of how their JIT compiler
works
[http://www.filpizlo.com/papers.html](http://www.filpizlo.com/papers.html)

------
wiradikusuma
Man, I always use String.intern() for synchronized(theString). --
[https://shipilev.net/jvm/anatomy-quarks/10-string-
intern/](https://shipilev.net/jvm/anatomy-quarks/10-string-intern/)

~~~
hyperpape
Aside from String.intern() being dubious, if you synchronized on an interned
string, anything else that happens to work with an interned copy of that
string is now contending with your lock. In the worst case, you could
deadlock.

If you really need this sort of dynamic locking, you can use a
ConcurrentHashMap<String, Object> to achieve it (lock on the object). I'm not
sure whether it's ever the _best_ design, but it avoids interning the string,
and it keeps an anonymous lock object that you know won't be shared.

~~~
chrisseaton
> anything else that happens to work with an interned copy of that string is
> now contending with your lock

Isn't that the point?

People use it to create symbolic locks in situations where they don't want to
use any more formal link of linkage provided by the JVM. In your case you need
some way to get a handle to that concurrent hash map, so some kind of formal
JVM linkage. Sometimes that's hard.

Not saying how it's how I'd chose to design an application, but I presume
people doing this have their own good reasons.

~~~
hyperpape
I think there's a subtle difference: you want to create mutual exclusion
around specific operations using that string. However, it's at least
conceivable that something else uses the interned instance of that string, and
thereby creates contention.

Now, if your string is sufficiently unique, the chances are relatively low, as
long as you don't leak a reference to it (an object, or explicit Lock only has
one purpose, so that's less likely).

Still, it's basically mysterious action at a distance. Whereas using the
ConcurrentHashMap, it's very explicit action at a distance. Granted, it does
require explicit JVM linkage, which is a cost.

------
MaxBarraclough
I'm not getting #17: _Trust Nonstatic Final Fields._

> what happens if someone changes that field?

The field is final. The static is final. How can the field be changed?
Reflection?

~~~
UncleMeat
Final applies to the field, not the object. You can still mutate the object
pointed at by a final field.

~~~
MaxBarraclough
Sure, but the instance's only field, is final.

The static reference _KNOWN_M_ is final, and the only field of the pointed-to
instance, _x_ , is also final.

