RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

Peter Levart peter.levart at gmail.com
Fri Jul 6 16:10:19 UTC 2018


Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
> I dispute "they will understand this might have happened in another 
> thread". 

What if the stack trace was like the following...

Before patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
         at ClinitFailure.lambda$main$0(ClinitFailure.java:20)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
         at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
         at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
         at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
         at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
         at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.RuntimeException: Can't get it!
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:12)
         ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:10)
         ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty
         at ClinitFailure.lambda$main$1(ClinitFailure.java:28)
         at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783)
         at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
         at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
         at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
         at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
         at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
         at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)


After patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
         at ClinitFailure.lambda$main$0(ClinitFailure.java:18)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
         at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
         at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
         at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
         at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
         at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.RuntimeException: Can't get it!
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:10)
         ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:8)
         ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty
         at 
java.base/java.lang.ClassLoader.throwReinitException(ClassLoader.java:3062)
         at ClinitFailure.lambda$main$1(ClinitFailure.java:25)
         at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783)
         at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
         at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
         at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
         at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
         at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
         at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.ExceptionInInitializerError: 11 ms ago in thread 
ForkJoinPool.commonPool-worker-3
         at ClinitFailure.lambda$main$0(ClinitFailure.java:18)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
         at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
         ... 5 more
Caused by: java.lang.RuntimeException: Can't get it!
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:10)
         ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0
         at ClinitFailure$Faulty.<clinit>(ClinitFailure.java:8)
         ... 8 more



This is what gets printed by the sample program:

public class ClinitFailure {

     static class Faulty {
         static {
             try {
                 int i = (new int[0])[1];
             } catch (Exception e) {
                 throw new RuntimeException("Can't get it!", e);
             }
         }
     }

     public static void main(String[] args) throws Exception {
         CompletableFuture.runAsync(() -> {
             try {
                 new Faulty();
             } catch (Throwable e) {
                 System.out.printf("\n1st attempt [%s]:\n\n", 
Thread.currentThread().getName());
                 e.printStackTrace(System.out);
             }
         }).thenRunAsync(() -> {
             try {
                 new Faulty();
             } catch (Throwable e) {
                 System.out.printf("\n2nd attempt [%s]:\n\n", 
Thread.currentThread().getName());
                 e.printStackTrace(System.out);
             }
         }).join();
     }
}


When the following patch is applied:

http://cr.openjdk.java.net/~plevart/jdk-dev/8203826_NoClassDefFoundError.cause/webrev.01/


I took Volker's patch and modified it a bit:

- The logic to construct and throw NoClassDefFoundError and to record 
initial <clinit> exception is in java now. It uses ClassLoaderValue 
internal API to save the chains of exception(s) for faulty classes. It 
is easier to do such logic in Java and less error prone.
- The chain of original <clinit> exception(s) is replaced with 
substitutes that mimic .toString() and .printStackTrace() methods of 
original chain, but don't reference any classes outside bootstrap class 
loader
- The replacement chain of original exceptions adds a custom message 
insert into the top exception as a hint to the user:

         java.lang.ExceptionInInitializerError: 11 ms ago in thread 
ForkJoinPool.commonPool-worker-3


So, what do you think of this one?

Regards, Peter




More information about the hotspot-runtime-dev mailing list