JDK-4966410 : Stacktrace missing from NullPointerException
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2003-12-10
  • Updated: 2004-11-25
  • Resolved: 2003-12-15
Related Reports
Duplicate :  
Description

Name: rmT116609			Date: 12/10/2003


FULL PRODUCT VERSION :
java version "1.4.2_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_01-b06)
Java HotSpot(TM) Client VM (build 1.4.2_01-b06, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux mattias-laptop 2.4.20 #4 SMP Fri Aug 8 09:48:27 CEST 2003 i686 i686 i386 GNU/Linux
(This Linux installation is a RedHat 9 with a modified kernel).

Windows 2000


A DESCRIPTION OF THE PROBLEM :
Under some (unclear) circumstances NullPointerExceptions can be thrown without any stacktrace. This makes the error handling within our system difficult. (The fact that the NullPointerException is thrown is not the bug - this is expected behaviour)

This only seems to happen when the JVM is run with "-server" option.

For more details on the problem see "Steps to Reproduce" below


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I have managed to create a small testcase that reproduces the issue. There is one Makefile and a source file "StackTraceTest.java".  I am pasting them below as text (ie in the "Source code..." entry).

With these files
1. Place the files in the current directory
2. Make sure JAVA_HOME environment variable is set
3. Run "make"
4. Run "make runtest"

One strange effect I noted was that if the line "import java.lang.reflect.*;" was removed from the test program, the problem seems to happen later (ie after about 6000 testruns instead of 2000). Should this even affect the generated class file in any way??


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The NullPointerException should always include stacktrace.
ACTUAL -
After a number of Ok testruns by the program (typically around 2000), the test results in a NullPointerException with no stacktrace. After the first failure, all subsequent runs seem to fail as well.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
...
****** validation failed. Thread 100, test run 9, total count 2181
java.lang.NullPointerException
 
****** validation failed. Thread 37, test run 23, total count 2182
java.lang.NullPointerException
 
****** validation failed. Thread 38, test run 23, total count 2183
java.lang.NullPointerException
...

Note the lack of stacktrace on the NullPointerExceptions.


REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
================== StackTraceTest.java =================

import java.lang.reflect.*;
import java.io.*;
import java.util.*;

public class StackTraceTest extends Thread
{
    public static void main( String[] args )
        throws Exception {
        
        System.out.println("Starting...");
        int threadCount = 100;
        int testCount = 100;

        StackTraceTest[] tests =
            new StackTraceTest[ threadCount ];
        for( int i=0; i<threadCount; ++i ) {
            tests[i] = new StackTraceTest();
        }
        for( int j=0; j<tests.length; ++j ) {
            tests[j].start();
        }
        for( int i=0; i<testCount; ++i ) {
            for( int j=0; j<tests.length; ++j ) {
                tests[j].addAction( RUN_TEST );
            }
        }
        for( int j=0; j<tests.length; ++j ) {
            tests[j].addAction( EXIT );
        }
    }
    
    private final static int NO_ACTION = 0;
    private final static int RUN_TEST = 1;
    private final static int EXIT = 2;

    private LinkedList _actionList = new LinkedList();

    private static int threadIdCounter = 0;
    private static synchronized int getThreadId() {
        return ++threadIdCounter;
    }

    private int _threadId = getThreadId();
    private int _testCount = 0;

    public void run() {

        int action;
        do {
            action = getAction();
            if( action == RUN_TEST ) {
                test(null);
            }
            try {
                // Wait 1/10 sec
                synchronized( this ) {
                    wait(100);
                }
            } catch( InterruptedException ignored ) {}
        } while( action != EXIT );
    }

    private synchronized int getAction() {
        if( _actionList.isEmpty() ) {
            return NO_ACTION;
        } else {
            return ((Integer)_actionList.removeFirst()).intValue();
        }
    }

    public synchronized void addAction( int action ) {
        _actionList.addLast( new Integer(action));
        notify();
    }

    private static String _refResult = null;
    private static int _totalTestCount = 0;
    private static synchronized void validateResult(
        String result, int threadId, int testCount ) {

        ++_totalTestCount;
        if( _totalTestCount % 1000 == 0 ) {
            System.out.println( "Total Count: "+_totalTestCount );
        }

        if( _refResult == null ) {
            _refResult = result;
        } else {
            if( ! _refResult.equals( result )) {
                System.out.println("****** validation failed. Thread "+
                                   threadId+", test run "+testCount+
                                   ", total count "+_totalTestCount);
                System.out.println(result);
            }
        }
    }

    public void test( String s ) {

        ++_testCount;
        try {
            "ABC".startsWith( s );
        }
        catch( Exception e ) {
            StringWriter sout = new StringWriter();
            e.printStackTrace(new PrintWriter(sout));
            String result = sout.toString();
            validateResult( result, _threadId, _testCount );
        }
    }
}

=============== Makefile ========================
StackTraceTest.class: StackTraceTest.java
	$(JAVA_HOME)/bin/javac $^

runtest:
	$(JAVA_HOME)/bin/java -server -cp . StackTraceTest

runtest_client:
	$(JAVA_HOME)/bin/java -client -cp . StackTraceTest


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Run JVM without "-server" flag
(Incident Review ID: 230192) 
======================================================================

Comments
WORK AROUND Run with -Xint ###@###.### 2003-12-15 Some observation that I have made For jdk 1.3.1 For -server Use -Xint or -Xcomp use -XX:-OmitStackTraceInFastThrow ( 1.3.1_14 and above ) Use client For jdk 1.4.2 For -server Use -Xint Use -Xcomp Use -XX:+FullSpeedJVMDI ( 1.4.2_05 and above ) or Use client For jdk 1.5 use -XX:-OmitStackTraceInFastThrow Use client ###@###.### 2004-11-25 05:23:42 GMT ###@###.### 2004-11-25 07:44:56 GMT
25-11-2004

EVALUATION Reproduced on Sparc: jaberwocky% java -server StackTraceTest Starting... Total Count: 1000 Total Count: 2000 Total Count: 3000 ****** validation failed. Thread 22, test run 40, total count 3968 java.lang.NullPointerException ****** validation failed. Thread 59, test run 41, total count 3969 java.lang.NullPointerException Pass to the runtime group. ###@###.### 2003-12-10 Running with java -server -Xint StackTraceTest test always passes for me. Running in mixed mode seems to trigger this exception. ###@###.### 2003-12-12 The testcase runs for a short bit in interpretted mode. Eventually the result or the method call gets hot thus a compile. Since their appears to be a difference between compiled exception string returned and interpretted string returned testcase fails. This appears to be a compiler bug or testcase issue.. This is reproducible in JDK 1.5.0-b31 latest... Assigning to compiler team for further review...
11-06-2004