JDK版本:
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)
如果我們撰寫如下代碼:
import java.util.concurrent.ThreadLocalRandom;
public class Test {
public static void main(String[] args) {
int exceptionCount = 0;
while (true) {
try {
Object object = null;
if (ThreadLocalRandom.current().nextInt(2) == 0) {
object = new Object();
}
object.hashCode();
} catch (Exception e) {
System.err.println( exceptionCount);
e.printStackTrace();
}
}
}
}
部分輸出是:
21123
java.lang.NullPointerException
at Test.main(Test.java:13)
21124
java.lang.NullPointerException
at Test.main(Test.java:13)
21125
java.lang.NullPointerException
21126
java.lang.NullPointerException
即例外發生21124次后不再列印堆疊幀。
我們對上面的代碼做一個小改動,注意nextInt
方法的引數:
import java.util.concurrent.ThreadLocalRandom;
public class Test {
public static void main(String[] args) {
int exceptionCount = 0;
while (true) {
try {
Object object = null;
if (ThreadLocalRandom.current().nextInt() == 0) {
object = new Object();
}
object.hashCode();
} catch (Exception e) {
System.err.println( exceptionCount);
e.printStackTrace();
}
}
}
}
部分輸出是:
115711
java.lang.NullPointerException
at Test.main(Test.java:13)
115712
java.lang.NullPointerException
at Test.main(Test.java:13)
115713
java.lang.NullPointerException
115714
java.lang.NullPointerException
即例外發生115712次后不再列印堆疊幀。
現在我想知道觸發省略例外堆疊幀的這個方法的執行次數是怎么計算的?
參考:
But in the case of a resolved Methodref, JVM throws NullPointerException
from a different place in the code. This another path records the fact of a thrown exception in the MethodData
structure:
MethodData
is a JVM structure that holds method's runtime profile to be used for JIT compilation. The presense of the profile makes JVM compile the method directly at tier 4 (because there is no need to collect profile at tier 3, if it was already collected in the interpreter).
In this case, when a number of loop iterations reaches 40000 (-XX:Tier4BackEdgeThreshold
), the method is scheduled for compilation at tier 4. But since the feedback step is 1024 (2Tier0BackedgeNotifyFreqLog), the actual number of iterations before JIT compilation is 40*1024 = 40960.
Summary
Getting back to your original example. When ThreadLocalRandom.current().nextInt(2) == 0
is true, hashCode()
is successfully resolved and executed. Subsequent NPEs are thrown from invokevirtual
implementation, and as a side effect, JVM creates MethodData
for the main
method. When the number of loop iterations exceeds 40000 (half of them resulting in a normal call, and half in a thrown exception), the method gets compiled by C2 with OmitStackTraceInFastThrow
optimization. Hence a little more than 20000 exceptions before the optimization.
On the contrary, ThreadLocalRandom.current().nextInt() == 0
is almost never true. Therefore hashCode()
call never succeeds: NPE is thrown while resolving the constant pool entry, and the entry remains unresolved. That's why the method is first compiled by C1 (after 60K iterations), then recompiled by C2 (after 40K more iterations). So the method throws more than 100K exceptions in total before OmitStackTraceInFastThrow
optimization takes place.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/442422.html