Следующий код,
public class TestFastThrow {
public static void main(String[] args) {
int count = 0;
int exceptionStackTraceSize = 0;
Exception exception = null;
do {
try {
throwsNPE(1);
}
catch (Exception e) {
exception = e;
if (exception.getStackTrace().length != 0) {
exceptionStackTraceSize = exception.getStackTrace().length;
count++;
}
}
}
while (exception.getStackTrace().length != 0);
System.out.println("Iterations to fastThrow :" + count + ", StackTraceSize :" + exceptionStackTraceSize);
}
static void throwsNPE(int callStackLength) {
throwsNPE(callStackLength, 0);
}
static void throwsNPE(int callStackLength, int count) {
if (count == callStackLength) {
((Object) null).getClass();
}
else {
throwsNPE(callStackLength, count + 1);
}
}
}
дает следующий вывод после многократного запуска,
Iterations to fastThrow :5517, StackTraceSize :4
Iterations to fastThrow :2825, StackTraceSize :5
Iterations to fastThrow :471033, StackTraceSize :6
Iterations to fastThrow :1731, StackTraceSize :7
Iterations to fastThrow :157094, StackTraceSize :10
.
.
.
Iterations to fastThrow :64587, StackTraceSize :20
Iterations to fastThrow :578, StackTraceSize :29
Подробности VM
Java HotSpot(TM) 64-Bit Server VM (11.0.5+10-LTS) for bsd-amd64 JRE (11.0.5+10-LTS)
-XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+LogCompilation -XX:+PrintAssembly
Удивительно, почему JIT требует гораздо больше итераций для оптимизации, если трассировка стека имеет четную длину?
Я включил журналы JIT и проанализировал с помощью jitwatch, но не смог найти ничего полезного, только временную шкалу, когда C1 и Компиляция C2, кажется, происходит позже для стековых трасс четного размера.
Временная шкала примерно такая (глядя на то, когда java.lang.Throwable.getStackTrace()
компилируется)
| StackSize | 10 | 11 |
|---------------|-------|-------|
| Queued for C1 | 1.099 | 1.012 |
| C1 | 1.318 | 1.162 |
| Queued for C2 | 1.446 | 1.192 |
| C2 | 1.495 | 1.325 |
Почему именно это происходит ? А какую эвристику использует JIT для быстрого броска?