Robert,

I think you need to start a discussion on serviceability-dev about the diagnostic challenges in this area before proposing API changes to java.lang.management that have wider implications and potential interop issues. It might be that your starting point is the thread dump instead.

-Alan

On 14/11/2020 16:26, Robert Lu 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:

// 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



Reply via email to