JDK-8206963 : [AOT] bug with multiple class loaders
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 9,10,11,12
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2018-07-10
  • Updated: 2019-11-29
  • Resolved: 2018-10-05
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 11 JDK 12
11.0.7-oracleFixed 12 b15Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
Kevin wrote:
When a class is loaded, jvm try to iterate all aot code cache to find if it's a defined symbol of shared library. But it doesn't check class loader carefully, so a class in aot cache could incorrectly access class data in another loader . Please check my test case.

cat sub1/T1.java
public class T1{
  public int xadd(int a,int b){
    int r=a+b+T2.foo;
    System.out.println(r);
    return r;
  }
}
cat sub1/T2.java
public class T2{
  public static int foo=300;
  public static void change(int newVal) {foo=newVal;}
}

compile them in sub1 directory and make a copy as sub2. Create a aot library for T1.class
jaotc --output aot.so T1.class

put the aot.so into project directory and create a test file

cat Main.java

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class Main{
    public static void main(String[] args)throws Exception{
        File file1=new File("./sub1");
        File file2=new File("./sub2");
        URL url1 = file1.toURI().toURL();
        URL url2 = file2.toURI().toURL();
        changeT2(url1);  // run with loader1
        run(url2);            // run with loader2
    }
    public static void changeT2(URL url) throws Exception{
        URL [] urls={url};
        URLClassLoader ucl = new URLClassLoader(urls);
        Class<?> c2 = ucl.loadClass("T2");
        Constructor<?> conc = c2.getConstructor();
        Object obj = conc.newInstance();
        Method m = c2.getMethod("change", int.class);
        m.invoke(obj, 0); // change T2.foo=0
    }
    public static void run(URL url) throws Exception{
        URL [] urls={url};
        URLClassLoader ucl = new URLClassLoader(urls);
        Class<?> c1 = ucl.loadClass("T1");
        Constructor<?> conc = c1.getConstructor();
        Object obj = conc.newInstance();
        Method m = c1.getMethod("xadd", int.class, int.class);
        m.invoke(obj, 0, 0);
    }
}

The whole project is like this:
> ls -l *
-rw-r--r-- 1 kuaiwei.kw users 108329 Jul  9 14:55 aot.so
-rw-r--r-- 1 kuaiwei.kw users   1613 Jul  9 14:55 Main.class
-rw-r--r-- 1 kuaiwei.kw users   1241 Jul  9 14:55 Main.java
sub1:
total 16
-rw-r--r-- 1 kuaiwei.kw users 398 Jul  9 14:55 T1.class
-rw-r--r-- 1 kuaiwei.kw users 118 Jul  9 14:55 T1.java
-rw-r--r-- 1 kuaiwei.kw users 321 Jul  9 14:55 T2.class
-rw-r--r-- 1 kuaiwei.kw users 103 Jul  9 14:55 T2.java
sub2:
total 16
-rw-r--r-- 1 kuaiwei.kw users 398 Jul  9 14:55 T1.class
-rw-r--r-- 1 kuaiwei.kw users 118 Jul  9 14:55 T1.java
-rw-r--r-- 1 kuaiwei.kw users 321 Jul  9 14:55 T2.class
-rw-r--r-- 1 kuaiwei.kw users 103 Jul  9 14:55 T2.java
run without aot
> java Main
300

run with aot
> java -XX:+UseAOT -XX:AOTLibrary=./aot.so Main
0
Comments
Fix request (11u) I would like to downport this for parity with 11.0.7-oracle. Applies clean.
28-11-2019

Testing passed.
05-10-2018

http://cr.openjdk.java.net/~kvn/8206963/webrev.01/
04-10-2018

I ran tests from JDK-8209192 and JDK-8209043 and they passed with this fix.
04-10-2018

After more investigation and discussion with Igor V. I suggest for AOT to not support custom loaders: diff -r 63b3d7989fde src/hotspot/share/aot/aotCodeHeap.cpp --- a/src/hotspot/share/aot/aotCodeHeap.cpp Wed Aug 22 21:48:39 2018 +0200 +++ b/src/hotspot/share/aot/aotCodeHeap.cpp Wed Oct 03 20:09:13 2018 -0700 @@ -723,6 +723,11 @@ NOT_PRODUCT( klasses_seen++; ) + // AOT does not support custom class loaders. + if (!ik->class_loader_data()->is_builtin_class_loader_data()) { + return false; + } + AOTKlassData* klass_data = find_klass(ik); if (klass_data == NULL) { return false; This fixed this bug and also ATR bugs: JDK-8209192 and JDK-8209043. I will also update AOT JEP to add this limitation.
04-10-2018

Note, I am talking only about referenced by a method classes. Method holders will have normal got cells.
28-09-2018

The more I think about this the more 'Solution one (got cells per method)' looks the right one. We can't do check without class resolution which search through particular path starting from method's holder class loader and moving to its parents. I don't want to duplicate that code in AOT. It is more complicated changes than simple disable all AOT methods when multiple class loaders are used. But we don't have a choice if we want to have aot methods even in simple case with default AppClassLoader.
28-09-2018

Simple approach does not work: http://cr.openjdk.java.net/~kvn/8206963/webrev.00/ Some simple aot tests failed with it because test class is loaded by AppClassLoader and system classes (used by test methods) are loaded by bootstrap.
28-09-2018

Seems like the easiest (and most performant) solution.
10-07-2018

Yes, Igor if you are talking about system classes they will be in modules but not all users classes will be in modules. It is still the issue. I am inclining to third solution. We currently already have list of all dependent methods which reference the class. We can set class loader for method's holder class if it is not set at the same time as we set it for referenced class. If class loaded is already set but does not match current class loader we will deoptimize AOT methods which have this conflict. It will make sure that a methods use klasses from the same class loader.
10-07-2018

ILW = AOT compiled code may not respect classloader isolation, edge case with multiple class loaders and AOT, don't use AOT = HLM = P3
10-07-2018

this is needed only when we compile jar/class files. when we compile a jigsaw module, this problem can't exist, as you can't mess w/ classloader in a modularized world.
10-07-2018

This is day one issue when multiply class loaders are used. We have only one pair got cell for each class in AOT lib. In this test T2 class loaded first and we assign him one class loader. At the same time we populate corresponding T2 got cell in AOT lib - and this is the issue. When T1 class loaded with an other loader and xadd() is executed it check for T2 class got cell. T2 got cell is not NULL and it points to T2 loaded before so xadd() uses it even so it has different class loader - bug. One solution would be to have separate got cells for referenced classes per class. An other - add runtime class loader check in AOTed code. Third - keep list of referenced classes and scan them to make sure they have the same class loader otherwise deoptimize - throw such AOT code away.
10-07-2018