JDK-8280772 : newInstanceFromSystemProperty() loads class in different ClassLoader with javac
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 12,17,18
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2022-01-25
  • Updated: 2022-02-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 19
19Unresolved
Description
A DESCRIPTION OF THE PROBLEM :
When we launch a Java class with "java FunBug.java", the Java class is loaded in the com.sun.tools.javac.launcher.Main$MemoryClassLoader. Furthermore, if we load the same class using reflectively using newInstanceFromSystemProperty() inside the ForkJoinPool, for example when we specify a threadFactory, then it will use the same class that we have loaded in the MemoryClassLoader and create an
instance to use as a thread factory. However, if we compile the class, and then run the Java class with "java FunBug.java", then the Java class is loaded in the MemoryClassLoader as before, but the class for the thread factory is loaded in the AppClassLoader. This means that we now have multiple class instances and
any static variables would be defined twice. It works fine if we run the Java class with "java FunBug" (without the .java extension) or if we do not compile it and run it with "java FunBug.java" (with the .java extension).

In Java 11, this condition caused the following error:

java -Djava.util.concurrent.ForkJoinPool.common.threadFactory=FunBug FunBug.java 
Created FunBug class in jdk.internal.loader.ClassLoaders$AppClassLoader@77556fd
        of jdk.internal.loader.ClassLoaders$PlatformClassLoader@3270d194
error: class found on application class path: FunBug

Since Java 12, it would instead load the class twice, giving mysterious bugs

REGRESSION : Last worked in version 11.0.14

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile the FunBug.java file with javac.
2. Run the code with java -Djava.util.concurrent.ForkJoinPool.common.threadFactory=FunBug FunBug.java


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Either:

Created FunBug class in com.sun.tools.javac.launcher.Main$MemoryClassLoader@35e2d654
        of jdk.internal.loader.ClassLoaders$AppClassLoader@77556fd
        of jdk.internal.loader.ClassLoaders$PlatformClassLoader@14cd1699

or 

Created FunBug class in jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd
        of jdk.internal.loader.ClassLoaders$PlatformClassLoader@3a4afd8d
ACTUAL -
Created FunBug class in com.sun.tools.javac.launcher.Main$MemoryClassLoader@6b0c2d26
        of jdk.internal.loader.ClassLoaders$AppClassLoader@75b84c92
        of jdk.internal.loader.ClassLoaders$PlatformClassLoader@646be2c3
Created FunBug class in jdk.internal.loader.ClassLoaders$AppClassLoader@75b84c92
        of jdk.internal.loader.ClassLoaders$PlatformClassLoader@646be2c3
Exception in thread "main" java.lang.NullPointerException
        at FunBug.newThread(FunBug.java:21)
        at java.base/java.util.concurrent.ForkJoinPool.createWorker(ForkJoinPool.java:1328)
        at java.base/java.util.concurrent.ForkJoinPool.tryAddWorker(ForkJoinPool.java:1352)
        at java.base/java.util.concurrent.ForkJoinPool.signalWork(ForkJoinPool.java:1476)
        at java.base/java.util.concurrent.ForkJoinPool.externalPush(ForkJoinPool.java:1903)
        at java.base/java.util.concurrent.ForkJoinTask.fork(ForkJoinTask.java:704)
        at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:306)
        at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:408)
        at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:736)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateParallel(ForEachOps.java:188)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.base/java.util.stream.IntPipeline.forEach(IntPipeline.java:439)
        at java.base/java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:596)
        at FunBug.main(FunBug.java:28)


---------- BEGIN SOURCE ----------
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.IntStream;

public class FunBug implements ForkJoinPool.ForkJoinWorkerThreadFactory {
    static {
        System.out.println("Created FunBug class in " +
                FunBug.class.getClassLoader());
        ClassLoader parent = FunBug.class.getClassLoader().getParent();
        while (parent != null) {
            System.out.println("\tof " + parent);
            parent = parent.getParent();
        }
    }

    private static ForkJoinPool.ForkJoinWorkerThreadFactory defaultFactory;

    @Override
    public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return defaultFactory.newThread(pool);
    }

    public static void main(String... args) {
        defaultFactory = ForkJoinPool.defaultForkJoinWorkerThreadFactory;
        IntStream.range(0, Runtime.getRuntime().availableProcessors() * 8)
                .parallel()
                .forEach(i -> LockSupport.parkNanos(100_000_000));
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't run the javac launcher with compiled classes. Use either "java FunBug" with compiled classes or "java FunBug.java" without compiled classes

FREQUENCY : always



Comments
Assigning to Jon for now as this seems to be the source code launcher and CL delegation when the same class is visible to both the memory CL and the application class loader.
27-01-2022

This has nothing to do with hotspot. Re-assigning to the launcher as it is an issue related to "source mode".
27-01-2022

Issue is reproduced. OS: Windows 10 JDK 11.0.4: Pass Output: Created FunBug class in jdk.internal.loader.ClassLoaders$AppClassLoader@707f7052 of jdk.internal.loader.ClassLoaders$PlatformClassLoader@65fb9ffc error: class found on application class path: FunBug JDK 12ea11: Pass JDK 12ea 13: Fail Output: Created FunBug class in com.sun.tools.javac.launcher.Main$MemoryClassLoader@1283bb96 of jdk.internal.loader.ClassLoaders$AppClassLoader@21588809 of jdk.internal.loader.ClassLoaders$PlatformClassLoader@7b98f307 Created FunBug class in jdk.internal.loader.ClassLoaders$AppClassLoader@21588809 of jdk.internal.loader.ClassLoaders$PlatformClassLoader@7b98f307 Exception in thread "main" java.lang.NullPointerException at FunBug.newThread(FunBug.java:21) at java.base/java.util.concurrent.ForkJoinPool.createWorker(ForkJoinPool.java:1328) at java.base/java.util.concurrent.ForkJoinPool.tryAddWorker(ForkJoinPool.java:1352) at java.base/java.util.concurrent.ForkJoinPool.signalWork(ForkJoinPool.java:1476) at java.base/java.util.concurrent.ForkJoinPool.externalPush(ForkJoinPool.java:1903) at java.base/java.util.concurrent.ForkJoinTask.fork(ForkJoinTask.java:704) at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:306) at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:408) at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:736) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159) at java.base/java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateParallel(ForEachOps.java:188) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233) at java.base/java.util.stream.IntPipeline.forEach(IntPipeline.java:439) at java.base/java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:596) at FunBug.main(FunBug.java:28) JDK 17.0.2 :Fail JDK 18ea31: Fail Moving it to dev team for further analysis.
27-01-2022