-
Bug
-
Resolution: Fixed
-
P4
-
openjdk8u
-
b05
-
x86_64
-
os_x
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8311377 | 21.0.1 | Mandy Chung | P4 | Resolved | Fixed | b02 |
JDK-8311253 | 21 | Mandy Chung | P4 | Resolved | Fixed | b30 |
java -version:
openjdk version "1.8.0_265"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_265-b01)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.265-b01, mixed mode)
sw_vers:
ProductName: Mac OS X
ProductVersion: 10.15.5
BuildVersion: 19F101
A DESCRIPTION OF THE PROBLEM :
I override getClassLoadingLock not to lock instance of ClassLoader.
However during resolve class in constant pool, OpenJDK 8u and OpenJDK master branch.
I think JavaDoc of ClassLoader says that ClassLoader locks result of getClassLoadingLock but does not lock instance of ClassLoader.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. compile the attached java code. entry point class is Main.
2. if execute without any argument, a class ('Helper2') is loaded with 'ClassLoader.loadClass', the program prints like the expected result
3. if execute with 'useLcd' as an argument, a class ('Helper2') is loaded with ldc instruction, the program prints like the actual result.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
lock of lock object already token
class Helper2: Loader@1540e19d
ACTUAL -
lock of loader already token
lock of lock object already token
class Helper2: Loader@1540e19d
---------- BEGIN SOURCE ----------
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Consumer;
import sun.misc.Unsafe;
class Loader extends URLClassLoader {
TimingHelper timing;
Object lock;
Loader(Object lock, URL[] urls, TimingHelper timing) {
super(urls, null);
this.timing = timing;
this.lock = lock;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("Helper2")) {
timing.on(1);
timing.on(4);
}
return super.findClass(name);
}
@Override
protected Object getClassLoadingLock(String className) {
// to remove deadlock
return lock;
}
}
class TimingHelper {
private int s;
private Timer t = new Timer(true);
public synchronized void on(int status) {
try {
while (s != status - 1) {
wait();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
s = status;
notifyAll();
}
}
class ChildHelper {
public static void loadSomeClass(boolean useLdc) throws ClassNotFoundException {
Class<?> clazz;
if (useLdc) {
clazz = Helper2.class;
} else {
clazz = ChildHelper.class.getClassLoader().loadClass("Helper2");
}
System.out.println(clazz + ": " + clazz.getClassLoader());
}
}
class Helper2 {
public static void loadSomeClass() {
System.out.println("Helper2");
}
}
class Main {
static Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe)f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean checkLockIsNotTaken(Object obj) {
boolean locked = false;
try {
locked = unsafe.tryMonitorEnter(obj);
} finally {
if (locked) unsafe.monitorExit(obj);
}
return !locked;
}
public static void main(String[] args) throws Exception {
boolean useLdc = args.length != 0 && args[0].equals("useLdc");
TimingHelper timing = new TimingHelper();
URL[] urls = new URL[]{ Main.class.getProtectionDomain().getCodeSource().getLocation() };
Object lock = new Object();
Loader child = new Loader(lock, urls, timing);
Method childHelpMethod = child.loadClass(ChildHelper.class.getName()).getMethod("loadSomeClass", boolean.class);
childHelpMethod.setAccessible(true);
Runnable childHelp = () -> {
try {
childHelpMethod.invoke(null, useLdc);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
new Thread(() -> {
childHelp.run();
}).start();
timing.on(2);
if (checkLockIsNotTaken(child)) System.out.println("lock of loader already token");
if (checkLockIsNotTaken(lock)) System.out.println("lock of lock object already token");
timing.on(3);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
If register the Loader as parallel capable, the code run as I expected currently but I want to know the correct workaround.
FREQUENCY : always
- backported by
-
JDK-8311253 Clarify the spec of ClassLoader::getClassLoadingLock for non-parallel capable loader
- Resolved
-
JDK-8311377 Clarify the spec of ClassLoader::getClassLoadingLock for non-parallel capable loader
- Resolved
- csr for
-
JDK-8311128 Clarify the spec of ClassLoader::getClassLoadingLock for non-parallel capable loader
- Closed
- relates to
-
JDK-4670071 java.lang.ClassLoader.loadClassInternal(String) is too restrictive
- Closed
- links to
-
Commit openjdk/jdk21/205dffe3
-
Commit openjdk/jdk/b9198f99
-
Review openjdk/jdk21/93
-
Review openjdk/jdk/14720