1
0
Эх сурвалжийг харах

HotThreads refactor idle threads code (#77805)

Refactor HotThreads.isIdleThread

Remove code duplication by using lists of named stack frames, split
JVM thread name checking and stack trace checking into separate
methods, add new JVM threads (Notification Thread, Common-Cleaner) into
the list of known JVM threads.
Nikola Grcevski 4 жил өмнө
parent
commit
2afec9ace7

+ 27 - 26
server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java

@@ -19,6 +19,7 @@ import java.lang.management.ThreadMXBean;
 import java.time.Clock;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
@@ -41,6 +42,20 @@ public class HotThreads {
     private ReportType type = ReportType.CPU;
     private boolean ignoreIdleThreads = true;
 
+    private static final List<String[]> knownIdleStackFrames = Arrays.asList(
+        new String[] {"java.util.concurrent.ThreadPoolExecutor", "getTask"},
+        new String[] {"sun.nio.ch.SelectorImpl", "select"},
+        new String[] {"org.elasticsearch.threadpool.ThreadPool$CachedTimeThread", "run"},
+        new String[] {"org.elasticsearch.indices.ttl.IndicesTTLService$Notifier", "await"},
+        new String[] {"java.util.concurrent.LinkedTransferQueue", "poll"},
+        new String[] {"com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout", "run"}
+    );
+
+    // NOTE: these are JVM dependent and JVM version dependent
+    private static final List<String> knownJDKInternalThreads = Arrays.asList(
+        "Signal Dispatcher", "Finalizer", "Reference Handler", "Notification Thread", "Common-Cleaner", "process reaper"
+    );
+
     public enum ReportType {
 
         CPU("cpu"),
@@ -103,37 +118,23 @@ public class HotThreads {
         }
     }
 
-    static boolean isIdleThread(ThreadInfo threadInfo) {
-        String threadName = threadInfo.getThreadName();
+    static boolean isKnownJDKThread(ThreadInfo threadInfo) {
+        return (knownJDKInternalThreads.stream().anyMatch(jvmThread ->
+            threadInfo.getThreadName() != null && threadInfo.getThreadName().equals(jvmThread)));
+    }
 
-        // NOTE: these are likely JVM dependent
-        if (threadName.equals("Signal Dispatcher") ||
-            threadName.equals("Finalizer") ||
-            threadName.equals("Reference Handler")) {
+    static boolean isKnownIdleStackFrame(String className, String methodName) {
+        return (knownIdleStackFrames.stream().anyMatch(pair ->
+            pair[0].equals(className) && pair[1].equals(methodName)));
+    }
+
+    static boolean isIdleThread(ThreadInfo threadInfo) {
+        if (isKnownJDKThread(threadInfo)) {
             return true;
         }
 
         for (StackTraceElement frame : threadInfo.getStackTrace()) {
-            String className = frame.getClassName();
-            String methodName = frame.getMethodName();
-            if (className.equals("java.util.concurrent.ThreadPoolExecutor") &&
-                methodName.equals("getTask")) {
-                return true;
-            }
-            if (className.equals("sun.nio.ch.SelectorImpl") &&
-                methodName.equals("select")) {
-                return true;
-            }
-            if (className.equals("org.elasticsearch.threadpool.ThreadPool$CachedTimeThread") &&
-                methodName.equals("run")) {
-                return true;
-            }
-            if (className.equals("org.elasticsearch.indices.ttl.IndicesTTLService$Notifier") &&
-                methodName.equals("await")) {
-                return true;
-            }
-            if (className.equals("java.util.concurrent.LinkedTransferQueue") &&
-                methodName.equals("poll")) {
+            if (isKnownIdleStackFrame(frame.getClassName(), frame.getMethodName())) {
                 return true;
             }
         }

+ 12 - 3
server/src/test/java/org/elasticsearch/monitor/jvm/HotThreadsTests.java

@@ -54,16 +54,19 @@ public class HotThreadsTests extends ESTestCase {
     }
 
     public void testIdleThreadsDetection() {
-        for (String threadName : new String[] { "Signal Dispatcher", "Finalizer", "Reference Handler" }) {
+        for (String threadName : new String[] {
+            "Signal Dispatcher", "Finalizer", "Reference Handler", "Notification Thread", "Common-Cleaner", "process reaper" }) {
             ThreadInfo mockedThreadInfo = mock(ThreadInfo.class);
             when(mockedThreadInfo.getThreadName()).thenReturn(threadName);
+            assertTrue(HotThreads.isKnownJDKThread(mockedThreadInfo));
             assertTrue(HotThreads.isIdleThread(mockedThreadInfo));
         }
 
-        for (String threadName : new String[] { "Notification Thread", "Common-Cleaner" }) {
+        for (String threadName : new String[] { "Text", "", null, "Finalizer".toLowerCase(Locale.ROOT) }) {
             ThreadInfo mockedThreadInfo = mock(ThreadInfo.class);
             when(mockedThreadInfo.getThreadName()).thenReturn(threadName);
             when(mockedThreadInfo.getStackTrace()).thenReturn(new StackTraceElement[0]);
+            assertFalse(HotThreads.isKnownJDKThread(mockedThreadInfo));
             assertFalse(HotThreads.isIdleThread(mockedThreadInfo));
         }
 
@@ -75,6 +78,10 @@ public class HotThreadsTests extends ESTestCase {
                 new String[]{"org.elasticsearch.monitor.testOther", "methodFour"}
             ));
 
+        for (StackTraceElement stackFrame : testJvmStack) {
+            assertFalse(HotThreads.isKnownIdleStackFrame(stackFrame.getClassName(), stackFrame.getMethodName()));
+        }
+
         ThreadInfo notIdleThread = mock(ThreadInfo.class);
         when(notIdleThread.getThreadName()).thenReturn("Not Idle Thread");
         when(notIdleThread.getStackTrace()).thenReturn(testJvmStack.toArray(new StackTraceElement[0]));
@@ -87,13 +94,15 @@ public class HotThreadsTests extends ESTestCase {
                 new String[]{"sun.nio.ch.SelectorImpl", "select"},
                 new String[]{"org.elasticsearch.threadpool.ThreadPool$CachedTimeThread", "run"},
                 new String[]{"org.elasticsearch.indices.ttl.IndicesTTLService$Notifier", "await"},
-                new String[]{"java.util.concurrent.LinkedTransferQueue", "poll"}
+                new String[]{"java.util.concurrent.LinkedTransferQueue", "poll"},
+                new String[]{"com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout", "run"}
             ));
 
         for (StackTraceElement extraFrame : idleThreadStackElements) {
             ThreadInfo idleThread = mock(ThreadInfo.class);
             when(idleThread.getThreadName()).thenReturn("Idle Thread");
             when(idleThread.getStackTrace()).thenReturn(new StackTraceElement[] {extraFrame});
+            assertTrue(HotThreads.isKnownIdleStackFrame(extraFrame.getClassName(), extraFrame.getMethodName()));
             assertTrue(HotThreads.isIdleThread(idleThread));
 
             List<StackTraceElement> topOfStack = new ArrayList<>(testJvmStack);