JDK-8188133 : C2: Static field accesses in clinit can trigger deoptimizations
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 8,9,10,11,12,13
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2017-09-26
  • Updated: 2019-10-12
  • Resolved: 2019-02-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 12 JDK 13
11.0.4Fixed 12.0.2Fixed 13 b07Fixed
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+170)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+170, mixed mode)

A DESCRIPTION OF THE PROBLEM :
Below is a small test case in which JIT compiler produces a very slow code even in comparison with interpreter.
The first 30-40 iterations of outer cycle runs quite fast, 2-3 ms each. Further it begins to degrade hundreds of times up do 600-1000 ms.
With -Xcomp option it works slowly from the very beginning.
Reproduced always on x64 JVM and with -server option on x86 JVM.
Reproduced on Java 8_05 as well. Didn't tested on Java 7.

The presence of lambda does not affect reproducibility, the code just shorter.

I associate the bug with the C2 compiler.

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached source sode.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class Test {
    static int n;
    static {
        Runnable r = () -> n = new Integer(0);
        for (int i=0; i<1000; ++i) {
            long t = System.currentTimeMillis();
            for (int j=0; j<100000; ++j) {
                r.run();
            }
            t = System.currentTimeMillis() - t;
            System.out.printf("%2d %6d\n", i, t);
        }
    }

    public static void main(String[] args) {
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not put the code in a static section.


Comments
Fix Request (12u, 11u) Backporting this fix partially recuperates the regressions accrued since 8. While this is technically not a regression fix, the performance improvements are substantial enough to cushion the blow elsewhere. Patch applies cleanly to 12u, and with wiggle to 11u. The original regression test improves dramatically for both 11u and 12u. Both 11u and 12u JDK passes tier1 tests. 11u RFR: https://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2019-April/033441.html
16-04-2019

This enhancement might partially resolve a large startup/performance regression seen in clojure apps since 11.0.2
20-02-2019

I'll repurpose this bug to track improvements in how static field accesses in clinit are handled by C2. Reclassified to Enhancement, since C2 always behaved that way. Regression between 8 & 9 will be tracked by JDK-8192070.
29-11-2017

static block is compiled, but inlning doesn't happen because the callee class (Test1) isn't initialized: 912 25 % b Test1::<clinit> @ 22 (81 bytes) @ 30 Test1$$Lambda$1/1709537756::run (4 bytes) inline (hot) \-> TypeProfile (11264/11264 counts) = Test1$$Lambda$1 @ 0 Test1::lambda$static$0 (15 bytes) failed initial checks
29-09-2017

C1 doesn't hit that because it relies on code patching and deoptimization/recompilation.
29-09-2017

It's not related to safepoints, but a pathological case with infinite deoptimization loop, which causes the slowdown. When C2 compiles the lambda (Test1::lambda$static$0), it puts uncommon trap for the write to Test1.n, because Test1 isn't initialized yet. On the first call of compiled method, the nmethod will be thrown away and recompiled again. It continues until the threshold on recompilations is hit (too many recompilations) and the uncommon trap doesn't trigger recompilation anymore. But it still does deoptimization on every invocation which is costly. $ java -XX:-TieredCompilation -XX:CICompilerCount=1 -Xbatch -XX:+PrintCompilation -XX:+TraceDeoptimization ... ... 1051 23 b Test1::lambda$static$0 (15 bytes) Uncommon trap bci=11 pc=0x000000010ee08958, relative_pc=0x0000000000000018, method=Test1.lambda$static$0()V, debug_id=0 Uncommon trap occurred in Test1::lambda$static$0 compiler=c2 compile_id=23 (@0x000000010ee08958) thread=6147 reason=uninitialized action=reinterpret unloaded_class_index=-1 debug_id=0 1055 23 Test1::lambda$static$0 (15 bytes) made not entrant DEOPT UNPACKING thread 0x00007f9f2b004800 vframeArray 0x00007f9f2c10c620 mode 2 {method} {0x000000011e9eb560} 'lambda$static$0' '()V' in 'Test1' - putstatic @ bci 11 sp = 0x00007000002191c0 1138 26 b Test1::lambda$static$0 (15 bytes) ... Uncommon trap bci=11 pc=0x000000010de301d8, relative_pc=0x0000000000000018, method=Test1.lambda$static$0()V, debug_id=0 Uncommon trap occurred in Test1::lambda$static$0 compiler=c2 compile_id=243 (@0x000000010de301d8) thread=6147 reason=uninitialized action=reinterpret unloaded_class_index=-1 debug_id=0 15307 243 Test1::lambda$static$0 (15 bytes) made not entrant DEOPT UNPACKING thread 0x00007fd3b5001800 vframeArray 0x00007fd3b48b5420 mode 2 {method} {0x000000011da0c560} 'lambda$static$0' '()V' in 'Test1' - putstatic @ bci 11 sp = 0x0000700000219210 Uncommon trap bci=11 pc=0x0000000111030458, relative_pc=0x0000000000000018, method=Test1.lambda$static$0()V, debug_id=0 Uncommon trap occurred in Test1::lambda$static$0 compiler=c2 compile_id=244 (@0x0000000111030458) thread=6147 reason=uninitialized action=none unloaded_class_index=-1 debug_id=0 DEOPT UNPACKING thread 0x00007f928a804800 vframeArray 0x00007f928b122220 mode 2 {method} {0x0000000120b8b560} 'lambda$static$0' '()V' in 'Test1' - putstatic @ bci 11 sp = 0x0000700000219210 Uncommon trap bci=11 pc=0x0000000111030458, relative_pc=0x0000000000000018, method=Test1.lambda$static$0()V, debug_id=0 Uncommon trap occurred in Test1::lambda$static$0 compiler=c2 compile_id=244 (@0x0000000111030458) thread=6147 reason=uninitialized action=none unloaded_class_index=-1 debug_id=0 DEOPT UNPACKING thread 0x00007f928a804800 vframeArray 0x00007f928b11e820 mode 2 {method} {0x0000000120b8b560} 'lambda$static$0' '()V' in 'Test1' - putstatic @ bci 11 sp = 0x0000700000219210 ...
29-09-2017

Okay, thanks for testing!
29-09-2017

It is not related to JDK-6869327 i verified with -XX:+UseCountedLoopSafepoints issue still exist
29-09-2017

This is most likely because C2 removes safepoints from counted loops (see JDK-6869327). Please check if the problem disappears with -XX:+UseCountedLoopSafepoints or if a non-counted loop is used.
29-09-2017

There is more performance degradation in 9 compared to 8. JDK8 - After 40 to 50th iteration 500-600 ms for every iteration JDK9 - After 40 to 50th iteration 1000-1100 ms for every iteration
29-09-2017

This issue is reproducible in both 8 and 9. JIT is making program to slow down and take longer time after 49th iteration. No issue observed on -Xint
29-09-2017