Withdrawn: 8253280: Use class name as class loading lock
Robert LU
github.com+1926185+robberphex at openjdk.java.net
Mon Jan 11 08:49:56 UTC 2021
On Thu, 10 Sep 2020 05:25:43 GMT, Robert LU <github.com+1926185+RobberPhex at openjdk.org> wrote:
> 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:
> Before patch:
> <details>
> <summary>Main.java</summary>
>
> // Main.java
> import java.io.PrintStream;
> import java.lang.management.*;
>
> public final class Main {
> private synchronized static void test1() {
> while (true) {
> try {
> Thread.sleep(1000);
> } catch (InterruptedException e) {
> e.printStackTrace();
> }
> }
> }
>
> private static void test() {
> while (true) {
> try {
> Main.class.getClassLoader().loadClass("java.lang.String");
> } catch (ClassNotFoundException e) {
> }
> }
> }
>
> public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
> new Thread(Main::test).start();
> new Thread(Main::test).start();
> new Thread(Main::test).start();
> new Thread(Main::test).start();
> new Thread(Main::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());
> return res;
> }
> }
> </details>
> After patch:
> <details>
> <summary>Main2.java</summary>
>
> // Main2.java
> import 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;
> }
> }
> </details>
This pull request has been closed without being integrated.
-------------
PR: https://git.openjdk.java.net/jdk/pull/104
More information about the serviceability-dev
mailing list