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