On Thu, 10 Sep 2020 05:25:43 GMT, Robert LU <github.com+1926185+robberp...@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@2e817b38 owned by > "Thread-0" Id=11 > at > java.base@15/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:616) > - blocked on java.lang.Object@2e817b38 > at > java.base@15/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604) > at > java.base@15/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168) > at java.base@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@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@724af044 owned by > "Thread-0" Id=12 > at > java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:646) > - blocked on java.lang.String@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