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

David Holmes david.holmes at oracle.com
Mon Jul 9 01:37:12 UTC 2018


Hi Peter,

On 7/07/2018 2:10 AM, Peter Levart wrote:
> 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...

Yes your suggestion makes it much clearer.

But ... my whole objection here is doing all this extraneous execution 
of Java code in response to the initial exception. The more Java code we 
execute the more likely we will hit secondary exceptions and the greater 
the possibility of unintended interactions that might lead back to the 
class that can't be initialized. I just don't think this level of effort 
is warranted.

Cheers,
David
-----

> 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