JDK-8169685 : C2: ClassCastException with empty message
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 7,8,9
  • Priority: P3
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 2016-11-13
  • Updated: 2021-09-06
  • Resolved: 2016-11-23
Related Reports
Relates :  
Relates :  
Description
 FULL PRODUCT VERSION :
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

EXTRA RELEVANT SYSTEM CONFIGURATION :
with eclipse Mars 2 
and with apache ant

A DESCRIPTION OF THE PROBLEM :
The ClassCastException is thrown on the same line of code:

      Double val = (Double)0.23f;

sometimes with message:

"java.lang.Float cannot be cast to java.lang.Double"

and sometimes with null message

REGRESSION.  Last worked in version 6u45

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the code snippet:

       public Integer g(Object data)
       {
           try
           {
               Double d = (Double)data;
           }
           catch(ClassCastException ex)
           {
            if( ex.getMessage() == null )
            {
              return 1;
            }
           }
           return 0;
       }

       @Test
       public void h()
       {
              Integer count = 0;
              for( Integer idx = 0; idx < 100000; idx++ )
              {
                     // The test
                     count += g(0.7312345f);
              }
              System.out.println("Total ClassCastException's with null message: "+count);
       }

and notice that much of the times the message in the exception is null

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
the counter should be 0


ACTUAL -
88k (out of 100k) were with null message of the ClassCastException

REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
run the code snippet:

       public Integer g(Object data)
       {
           try
           {
               Double d = (Double)data;
           }
           catch(ClassCastException ex)
           {
            if( ex.getMessage() == null )
            {
              return 1;
            }
           }
           return 0;
       }

       @Test
       public void h()
       {
              Integer count = 0;
              for( Integer idx = 0; idx < 100000; idx++ )
              {
                     // The test
                     count += g(0.7312345f);
              }
              System.out.println("Total ClassCastException's with null message: "+count);
       }

and notice that much of the times the message in the exception is null
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
workaround is to add -Xint to JVM flags to work in interpreter mode but this slows the processing substantially of course

note that when debugger is attached in eclipse - this phenomenon does not occur - maybe the code is interpreted when debugger is attached...


Comments
The problem reported by the customer is caused by a performance optimization. That optimization is enabled by default by the -XX:+OmitStackTraceInFastThrow. Disabling that optimization would result in a performance degradation for most of our customers. Therefore, we prefer to keep it on. If non-null messages are important for the customer, the customer is recommended to run the VM with the -XX:-OmitStackTraceInFastThrow option as a workaround. I'll close this issue as "Will not fix".
23-11-2016

More details: The problem is caused by a performance optimization performed by C2. To speed up execution of methods that frequently throw exceptions, the C2 compiled generates code that uses a pre-built exception (pre-built at compile time). The pre-built exception does not contain neither a stack trace nor a message detailing the exception. Here is the code that implements that functionality: http://hg.openjdk.java.net/jdk9/hs/hotspot/file/6f8884f94c25/src/share/vm/opto/graphKit.cpp#l555 If pre-built exceptions are not used, exceptions are handled by deoptimizing and continuing execution in the interpreter. That is slower than "staying" in compiled code, but it results in more accurate error messages (i.e., exceptions generated by the interpreter contain an error message and a stack trace as well). Using pre-built exceptions can be disabled with -XX:-OmitStackTraceInFastThrow. Using that flag is better than running the VM in interpreted mode, as it forces only methods throwing exceptions frequently to be executed in the interpreter. It's unclear how important accurate error messages are for the customer. It's also unclear how fixing this issue fits into the priorities of the compiler team. Will check the latter and get back with details.
22-11-2016

Updated ILW: - Behavior of compiled and interpreted code inconsistent with respect to exception reporting: C2-compiled code does not report stack traces and messages with exceptions thrown; - Seems to be an edge case: It is not yet clear how important how the contents of exception messages is for the customer; - Workaround: run with -XX:-OmitStackTraceInFastThrow; running with that option increases execution time of the affected method M.h() by roughly 15x, but the performance of other methods is unaffected (which would not be the case when running only with the interpreter). MMM=P3
22-11-2016

ILW=behavior of compiled and interpreted code inconsistent with respect to exception reporting,one test so far,no real workaround (only running with -Xint)=MMH=P3
15-11-2016

This is really not a regression, but one thing to notice that, in 8 and 9, exception with null message generated is very high 54k+/100k (submitter claims it is 88k), where as in 7 and 6 it always shows 1/100k times executed.
15-11-2016

Exception with null message is seen, it doesn't happen during debugging and -Xint mode Attached code prints the number of times exception generated with null message, below is the result == -sh-4.1$ /opt/java/jdk1.8.0_112/bin/javac Test.java -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java Test Total ClassCastException's with null message: 55504 -sh-4.1$ /opt/java/jdk1.8.0_112/bin/java -Xint Test Total ClassCastException's with null message: 0 -sh-4.1$ /opt/java/jdk-9_ea-144/bin/javac Test.java -sh-4.1$ /opt/java/jdk-9_ea-144/bin/java Test Total ClassCastException's with null message: 54661 -sh-4.1$ /opt/java/jdk-9_ea-144/bin/java -Xint Test Total ClassCastException's with null message: 0 ==
15-11-2016