JDK-6912517 : JIT bug compiles out (and stops running) code that needs to be run. Causes NPE.
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 6u14,6u16
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS:
    linux_redhat_4.0,linux_redhat_5.2,solaris_10 linux_redhat_4.0,linux_redhat_5.2,solaris_10
  • CPU: x86,sparc
  • Submitted: 2009-12-22
  • Updated: 2011-03-08
  • Resolved: 2011-03-08
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 6 JDK 7 Other
6u21Fixed 7Fixed hs17Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) Server VM (build 14.0-b16, mixed mode)


FULL OS VERSION :
SunOS desb0028.nyc.deshaw.com 5.10 127128-11 i86pc i386 i86pc


A DESCRIPTION OF THE PROBLEM :
The JIT compile kicks in and compiles the method ensureProperCallingThread() below into a version that null pointers.  This should never happen, and the JIT is producing incorrect code.

If you compile this bug on Solaris 10, and then run with:

java -d64 -XX:CompileThreshold=100 JITBug



THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See description

EXPECTED VERSUS ACTUAL BEHAVIOR :
Should not compile this code in such a way that it null pointers.
ERROR MESSAGES/STACK TRACES THAT OCCUR :

Eventually (may take a few runs) you will get this error:
Starting thread: 0
Starting thread: 1
Starting thread: 2
Exception in thread "Runner: 2" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)
Starting thread: 3
Exception in thread "Runner: 3" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)
Starting thread: 4
Exception in thread "Runner: 4" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)



REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------

/**
 * Highlights a bug with the JIT compiler.
 * @author Matt Bruce m b r u c e __\at/__ g m a i l DOT c o m
 */
public class JITBug implements Runnable
{
    private final Thread myThread;
    private Thread       myInitialThread;
    private boolean      myShouldCheckThreads;

    /**
     * Sets up the running thread, and starts it.
     */
    public JITBug(int id)
    {
        myThread = new Thread(this);
        myThread.setName("Runner: " + id);
        myThread.start();
        myShouldCheckThreads = false;
    }

    /**
     * @param shouldCheckThreads the shouldCheckThreads to set
     */
    public void setShouldCheckThreads(boolean shouldCheckThreads)
    {
        myShouldCheckThreads = shouldCheckThreads;
    }

    /**
     * Starts up the two threads with enough delay between them for JIT to
     * kick in.
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException
    {
        // let this run for a bit, so the "run" below is JITTed.
        for (int id = 0; id < 20; id++) {
            System.out.println("Starting thread: " + id);
            JITBug bug = new JITBug(id);
            bug.setShouldCheckThreads(true);
            Thread.sleep(2500);
        }
    }

    /**
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run()
    {
        long runNumber = 0;
        while (true) {
            // run hot for a little while, give JIT time to kick in to this loop.
            // then run less hot.
            if (runNumber > 15000) {
                try {
                    Thread.sleep(5);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            runNumber++;
            ensureProperCallingThread();
        }
    }

    private void ensureProperCallingThread()
    {
        // this should never be null.  but with the JIT bug, it will be.
        // JIT BUG IS HERE ==>>>>>
        if (myShouldCheckThreads) {
            if (myInitialThread == null) {
                myInitialThread = Thread.currentThread();
            }
            else if (myInitialThread != Thread.currentThread()) {
                System.out.println("Not working: " + myInitialThread.getName());
            }
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Change compile threshold and ensure that the code is JIT compiled only after at least two threads have used the code.

Comments
PUBLIC COMMENTS Problem: Block::implicit_null_check() does not check that the value is used in memory expression in instructions with embedded memory load. In the bug case the instruction was: CmpP val,(tls+off) Solution: Add the missing check. Add regression test.
08-01-2010

EVALUATION http://hg.openjdk.java.net/jdk7/hotspot-comp/hotspot/rev/1271af4ec18c
08-01-2010

EVALUATION The explicit check of "if (myInitialThread == null)" was replaced with incorrect implicit check: 03d B4: # B28 B5 <- B3 Freq: 1e+06 03d movq R10, [R10 + #32 (8-bit)] # ptr ! Field JITBug.myInitialThread 041 # TLS is in R15 041 cmpq R10, [R15 + #448 (32-bit)] # raw ptr 048 NullCheck R10 048 The code should be (generated by latest HS17 with -XX:+UseCompressedOops): 04e B4: # B7 B5 <- B3 Freq: 1e+06 04e movl R10, [RBP + #20 (8-bit)] # compressed ptr ! Field JITBug.myInitialThread 052 # TLS is in R15 052 movq R11, [R15 + #456 (32-bit)] # ptr 059 testl R10, R10 # compressed ptr 05c je,s B7 P=0.009950 C=201.000000 05c
06-01-2010

PUBLIC COMMENTS Can the submitter please: - clarify what line 99 (as per the stacktrace) corresponds to in the actual code. - confirm whether this only fails on 64-bit or whether 32-bit as well - how many processors are on the target machine The ability to reproduce this may well depend on the actual execution environment. Can the submitter also please correct the test program to use proper synchronization. The mutable field myShouldCheckThreads is set by the main thread and read by the target thread, and this needs to be done under synchronization, such as by declaring the field volatile private volatile boolean myShouldCheckThreads; After doing that please see if the problem still reproduces. I don't think this is a likely cause of the problem as described but it may have an impact.
22-12-2009