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.