RFR: 8253280: Use class name as class loading lock
Robert Lu
robberphex at gmail.com
Sat Nov 14 16:26:01 UTC 2020
When many thread try to load same class, the thread will stuck on
ClassLoader.loadClass.
At current jdk, the stacktrace by example program is:
"Thread-1" prio=5 Id=12 BLOCKED on java.lang.Object at 2e817b38 owned by
"Thread-0" Id=11
at java.base at 15/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:616)
- blocked on java.lang.Object at 2e817b38
at java.base at 15/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604)
at java.base at 15/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base at 15/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at app//Main.test(Main.java:19)
at app//Main$$Lambda$2/0x0000000800b8c468.run(Unknown Source)
at java.base at 15/java.lang.Thread.run(Thread.java:832)
There is no way to get which class stuck the thread.
*After this patch, the stacktrace will be*:
"Thread-1" prio=5 Id=13 BLOCKED on java.lang.String at 724af044 owned by
"Thread-0" Id=12
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:646)
- blocked on java.lang.String at 724af044 val="java.lang.String"
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:634)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
at app//Main2.test(Main2.java:19)
at app//Main2$$Lambda$37/0x00000001000c2a20.run(Unknown Source)
at java.base/java.lang.Thread.run(Thread.java:831)
That is, user will know which class stuck the thread, in this example, the
class is java.lang.String. It's helpful for troubleshooting.
The example program:
// Main2.javaimport java.io.PrintStream;import java.lang.management.*;
public final class Main2 {
private synchronized static void test1() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void test() {
while (true) {
try {
Main2.class.getClassLoader().loadClass("java.lang.String");
} catch (ClassNotFoundException e) {
}
}
}
public static void main(String[] args) throws
InterruptedException, NoSuchFieldException, IllegalAccessException {
new Thread(Main2::test).start();
new Thread(Main2::test).start();
new Thread(Main2::test).start();
new Thread(Main2::test).start();
new Thread(Main2::test).start();
while (true) {
Thread.sleep(1000);
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);
for (ThreadInfo info : infos) {
System.out.println(printThreadInfo(info));
}
}
}
private static String printThreadInfo(ThreadInfo threadInfo) {
StringBuilder sb = new StringBuilder("\"" +
threadInfo.getThreadName() + "\"" +
(threadInfo.isDaemon() ? " daemon" : "") +
" prio=" + threadInfo.getPriority() +
" Id=" + threadInfo.getThreadId() + " " +
threadInfo.getThreadState());
if (threadInfo.getLockName() != null) {
sb.append(" on " + threadInfo.getLockName());
}
if (threadInfo.getLockOwnerName() != null) {
sb.append(" owned by \"" + threadInfo.getLockOwnerName() +
"\" Id=" + threadInfo.getLockOwnerId());
}
if (threadInfo.isSuspended()) {
sb.append(" (suspended)");
}
if (threadInfo.isInNative()) {
sb.append(" (in native)");
}
sb.append('\n');
int i = 0;
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
for (; i < stackTrace.length; i++) {
StackTraceElement ste = stackTrace[i];
sb.append("\tat " + ste.toString());
sb.append('\n');
if (i == 0 && threadInfo.getLockInfo() != null) {
Thread.State ts = threadInfo.getThreadState();
switch (ts) {
case BLOCKED:
sb.append("\t- blocked on " +
printLockInfo(threadInfo.getLockInfo()));
sb.append('\n');
break;
case WAITING:
sb.append("\t- waiting on " +
printLockInfo(threadInfo.getLockInfo()));
sb.append('\n');
break;
case TIMED_WAITING:
sb.append("\t- waiting on " +
printLockInfo(threadInfo.getLockInfo()));
sb.append('\n');
break;
default:
}
}
for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
if (mi.getLockedStackDepth() == i) {
sb.append("\t- locked " + printLockInfo(mi));
sb.append('\n');
}
}
}
if (i < stackTrace.length) {
sb.append("\t...");
sb.append('\n');
}
LockInfo[] locks = threadInfo.getLockedSynchronizers();
if (locks.length > 0) {
sb.append("\n\tNumber of locked synchronizers = " + locks.length);
sb.append('\n');
for (LockInfo li : locks) {
sb.append("\t- " + printLockInfo(li));
sb.append('\n');
}
}
sb.append('\n');
return sb.toString();
}
private static String printLockInfo(LockInfo li) {
String res = li.getClassName() + '@' +
Integer.toHexString(li.getIdentityHashCode());
// There is no getLock method in current jdk
if (li.getStringValue() != null) {
return res + " val=\"" + li.getStringValue() + "\"";
}
return res;
}
}
----------
Commit messages:
- 8253280: Use class name as class loading lock
Changes: https://github.com/openjdk/jdk/pull/104/files
Webrev: https://openjdk.github.io/cr/?repo=jdk&pr=104&range=00
Issue: https://bugs.openjdk.java.net/browse/JDK-8253280
Stats: 74 lines changed; 73 ins; 1 del
Patch: https://git.openjdk.java.net/jdk/pull/104.diff
Fetch: git fetch https://git.openjdk.java.net/jdk pull/104/head:pull/104
PR: https://git.openjdk.java.net/jdk/pull/104
--
Robert Lu <robberphex at gmail.com>
More information about the core-libs-dev
mailing list