Read Online
Abstract:
Java 9 is more strict about what system internal classes we can use. So how can we use @Contended in Java 9? This article shows you how. Welcome to the 249th edition of The Java(tm) Specialists' Newsletter, sent to you from the Island of Crete. To the left of where I'm sitting, Mr Kostas' grapes are ripening in the sun, ready to be turned into Cretan village wine and tzikoudia. He acted in "Zorba the Greek" as a little boy. Now he is a grandfather in his 70s. If you've never seen "Zorba the Greek", you owe it to yourself to watch this classic. If anything, you will see how my neighbourhood looked a long time ago :-)
Dr Heinz's Extreme Java Courses: We offer all our courses as In-house and Self-Study options. In-house is best for large teams of Java experts. Self-study is suitable for individual programmers who want to advance their careers. If you have any questions, please simply reply to this email :-)
@Contended @since 9
A few months ago, I was trying to figure out how to use @Contended
in Java 9. I was thwarted. Then, whilst preparing for this month's webinar, I finally managed. Since it is highly likely that I will forget how, I thought it would be useful to write it down as a newsletter. That way, some time in the future, when I "google" "@Contended Java 9" I will hopefully find my article :-)
A little bit of background re @Contended
. It is an annotation added in Java 8 to pad fields. The reason why this can be helpful is to avoid false sharing in cache lines. @Contended
is a sun.misc
annotation, which means that we should ideally not use it in our code. There are techniques for padding without resorting to @Contended
, using some weird tricks to guarantee that the padding is in the correct place. See Blackhole class in JMH.
This newsletter is not going to answer the question of "why?" we would want to make some fields @Contended
, nor the questions "which?" and "when?". I'm assuming you know this already. If you don't, I'd suggest you try Extreme Java - Concurrency Performance by Heinz Kabutz, Mechanical Sympathy Blog by Martin Thompson and Psychosomatic, Lobotomy, Saw Blog by Nitsan Wakart. The only question I'm going to answer is "how?" do you do that in Java 9?
In Java 8, we could mark a field as @Contended
simply like so:
public class Cell { @sun.misc.Contended private volatile long value; }
When we compile our class in Java 8, we saw this warning, but it compiled without error:
heinz$ javac Cell.java Cell.java:2: warning: Contended is internal proprietary API and may be removed in a future release @sun.misc.Contended ^ 1 warning
By default, since our class is not in the bootclasspath, this annotation would have no effect. Here is some of the output when we run jol internals on our Cell:
Cell object internals: OFFSET SIZE TYPE DESCRIPTION 0 4 (object header) 4 4 (object header) 8 4 (object header) 12 4 (alignment/padding gap) 16 8 long Cell.value Instance size: 24 bytes Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
However, we can make the @Contended annotation take effect when we launch jol with the -XX:-RestrictContended JVM flag.
Cell object internals: OFFSET SIZE TYPE DESCRIPTION 0 4 (object header) 4 4 (object header) 8 4 (object header) 12 132 (alignment/padding gap) 144 8 long Cell.value 152 128 (loss due to the next object alignment) Instance size: 280 bytes Space losses: 132 bytes internal + 128 bytes external = 260 bytes total
As you can see, objects of type Cell use far more memory when a field is @Contended.
Here are some classes that use @Contended in Java 8:
- Thread (for ThreadLocalRandom fields)
- ConcurrentHashMap
- LongAdder and LongAccumulator (in their superclass Striped64)
- Exchanger (which I've never used)
- ForkJoinPool
Enough Already, What About Java 9?
Java 9 tries to stop us from shooting ourselves in the foot. Classes like sun.misc.Contended
, sun.misc.Unsafe
and sun.misc.Cleaner
have been marched out of publicly available packages and into jdk.internal.**
, where no non-Oracle programmer may enter. They relented a bit by leaving sun.misc.Unsafe
for us to play with, with the promise that it will be gone in Java 10, once we've moved all our code over to VarHandle.
Thus, if you change the package to jdk.internal.**
, our class will no longer compile:
public class Cell { @jdk.internal.vm.annotation.Contended private volatile long value; }
And now javac has the audacity to tell us:
heinz$ javac Cell.java Cell.java:2: error: package jdk.internal.vm.annotation is not visible @jdk.internal.vm.annotation.Contended ^ (package jdk.internal.vm.annotation is declared in module java.base, which does not export it to the unnamed module) 1 error
The java and javac commands have some additional flags that allow us access to restricted packages. Since we have not defined a module for our Cell class, it is in the "unnamed" module. We can export it to our "unnamed" module with the additional javac flag --add-exports java.base/jdk.internal.vm.annotation=ALL-UNNAMED
Of course, if your class is in a module, you'll have to specify that instead of ALL-UNNAMED.
heinz$ javac --add-exports \ java.base/jdk.internal.vm.annotation=ALL-UNNAMED Cell.java
No error. No warning. Bliss.
--add-exports vs --add-opens
Sometimes we might want to do some deep reflection on members of other modules. We can open up other modules with --add-opens, giving us access to private fields, methods, etc. The --add-opens removes more barriers than --add-exports. In fact, --add-opens implies --add-exports. It would not make sense to allow --add-opens with the javac command, as the compiler should anyway not allow us to access the private members of other classes. This is why javac only allows --add-exports (allowing us to access public members of non-exported classes).
Looking back at the various versions of Java, I think migrating to Java 9 will give us the most amount of work for the least benefit. It is scary how many companies are still stuck on Java 6 or 7. Even though the 6->7 and 7->8 migrations were relatively painless, it is still taking companies a very long time to move. However, we don't have a choice. We have to move forward. Eventually your version of Java will not be supported anymore. Such is life :-)
P.S. I also spoke about @Contended in this month's "Heinz's Happy Hour". You can find all our recordings here.