I have next sample of code:
class Shared {
int x;
int y;
void increment() {
x++;
y++;
}
void check() {
if (y > x) {
System.out.println("Ooops! y > x");
}
}
}
Looks clear? But main problem happens here when i am trying to increment and check in two threads:
Shared shared = new Shared();
Thread writer = new Thread(() -> {
for (int i = 0; i < N; i++) {
shared.increment();
}
});
Thread reader = new Thread(() -> {
for (int i = 0; i < N; i++) {
shared.check();
}
});
writer.start();
reader.start();
You can notice data race (instructions reordering in some cases?):
1. x++;
2. y++;
And now, i know about special VM flags, which can help me to print JIT compiler logs (-XX:+PrintCompilation
).
...
120 181 3 Shared::increment (21 bytes)
120 182 3 Shared::check (20 bytes)
120 183 4 Shared::increment (21 bytes)
120 184 4 Shared::check (20 bytes)
121 181 3 Shared::increment (21 bytes) made not entrant
121 182 3 Shared::check (20 bytes) made not entrant
121 185 n 0 java.lang.invoke.MethodHandle::linkToStatic(L)L (native) (static)
121 184 4 Shared::check (20 bytes) made not entrant
122 186 3 Shared::check (20 bytes)
122 187 n 0 java.lang.Object::clone (native)
122 188 4 Shared::check (20 bytes)
122 189 % 3 Main::lambda$main$0 @ 2 (19 bytes)
122 190 3 Main::lambda$main$0 (19 bytes)
123 186 3 Shared::check (20 bytes) made not entrant
...
OK, now i can see how compilation of increment method was processed:
120 181 3 Shared::increment (21 bytes)
120 183 4 Shared::increment (21 bytes)
121 181 3 Shared::increment (21 bytes) made not entrant
Do i understand correct, that reordering here is due of tiered compilation? Because increment()
- hot method, JIT compiler profiles this information and use C2
server compiler. And, as i think, reorder some instructions in such way, but in some cases optimization happens(made not entrant
). Or it's wrong?
Also, there are some another logs for compilation:
138 182 2 Shared::increment (21 bytes)
138 184 4 Shared::increment (21 bytes)
138 182 2 Shared::increment (21 bytes) made not entrant