Different classloaders in jdk8 and jdk11

See original GitHub issue

I’m upgrading a Spring Boot 2.2.2 application from Java 8 to 11.
I’ve faced some ClassNotFoundExceptions because Java 11 doesn’t include java ee packages, but after adding the missing jars, the application still cannot find the classes if the thread is created using CompletableFuture.

The classloaders in these CompletableFuture-threads are different if you execute with jdk8 or jdk11.
So there are ClassNotFoundException only inside the CompletableFuture thread.

I’ve checked twice the different migration guides and the issues fixed and cannot found this as a regular behavior so i file it as a bug.

/*
 * Java 8  http-nio-8080-exec-1 TomcatEmbeddedWebappClassLoader
 * Java 11 http-nio-8080-exec-1 TomcatEmbeddedWebappClassLoader
 */
 log.info(Thread.currentThread().getName()+" "+Thread.currentThread().getContextClassLoader().toString());

/*
 * Java 8  ForkJoinPool.commonPool-worker-1 TomcatEmbeddedWebappClassLoader
 * Java 11 ForkJoinPool.commonPool-worker-3 jdk.internal.loader.ClassLoaders$AppClassLoader@5bc2b487 <- it's different and it cannot load the classes even if the classes are in the final jar
 */
CompletableFuture.runAsync(() -> {
    log.info(Thread.currentThread().getName()+" "+Thread.currentThread().getContextClassLoader().toString());
}).join();

Related https://github.com/spring-projects/spring-boot/issues/17796

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:4
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
elabcommented, Jan 21, 2021

Parallel streams use the default ForkJoinPool.commonPool causing exact the same problem with class loaders in JDK9, JDK10, JDK11, … Watch out for parallelStream(), parallel() in your code.

In my case, it manifested in the error javax.xml.soap.SOAPException: Unable to create SAAJ meta-factory: Provider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found after switching from Java 8 to Java 11.

The error only occurred when the application was running packaged as JAR (and not in the Eclipse IDE).

UPDATE 2021-01-21: I ended up using ParallelCollectors (perhaps it can be useful to someone):

before:

return list.parallelStream().map(s -> s.myMapFunction()).collect(Collectors.toList());

now:

ExecutorService executor = Executors.newCachedThreadPool();
try {
    return list.stream().collect(ParallelCollectors.parallel(s -> s.myMapFunction(), Collectors.toList(), executor, parallelism)).join();
} finally {
    executor.shutdown();
}

The Thread Context Class Loader (TCCL) in the parallel threads is TomcatEmbeddedWebappClassLoader (whose parent is org.springframework.boot.loader.LaunchedURLClassLoader). Exactly as it was with JDK8. No class loading problems occure anymore.

JAR-packaged stand-alone Spring Boot application (embedded Tomcat), JDK11.

1reaction
ztomiccommented, Dec 3, 2020

Parallel streams use the default ForkJoinPool.commonPool causing exact the same problem with class loaders in JDK9, JDK10, JDK11, … Watch out for parallelStream(), parallel() in your code.

In my case, it manifested in the error javax.xml.soap.SOAPException: Unable to create SAAJ meta-factory: Provider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found after switching from Java 8 to Java 11.

The error only occurred when the application was running packaged as JAR (and not in the Eclipse IDE).

@elab I’ve had same issue with JAX-WS and ForkJoinPool and my workaround way using custom ForkJoinThreadFactory and now it is working fine (https://stackoverflow.com/a/57551188/5599629). And also I had to set javax.xml.soap.MetaFactory and javax.xml.soap.SAAJMetaFactory and javax.xml.bind.JAXBContextFactory system properties because without ForkJoinPool also it was working OK for couple of requests and after that ClassNotFoundException returned.

(sorry for commenting on closed issue)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Transition from Java 8 to Java 11 - Azure - Microsoft Learn
The class loader hierarchy has changed in Java 11. The system class loader (also known as the application class loader) is now an...
Read more >
ClassLoader (Java SE 12 & JDK 12 ) - Oracle Help Center
A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name...
Read more >
[#LPS-89149] Handle class loader changes in JDK11 which ...
Handle class loader changes in JDK11 which affect NewEnvTestRule ... Also, the bootstrap class loader in JDK11 defines much fewer classes compared to...
Read more >
Notes on upgrading from Java 8 to Java 11 - JavaIsland
Internally, the JVM can make use of modules by making class loading more efficient. The result is a smaller, lighter and faster start-up...
Read more >
Class Loaders in Java - Baeldung
As we can see, there are three different class loaders here: application, extension, and bootstrap (displayed as null). The application class ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found