8223306: Remove threads linked list (use ThreadsList's array in SA)
authorrehn
Tue, 21 May 2019 10:34:57 +0200
changeset 54955 46409371a691
parent 54954 6ec71a88b68e
child 54956 43340a79840d
8223306: Remove threads linked list (use ThreadsList's array in SA) Reviewed-by: coleenp, dholmes, dcubed
src/hotspot/share/runtime/thread.cpp
src/hotspot/share/runtime/thread.hpp
src/hotspot/share/runtime/threadSMR.hpp
src/hotspot/share/runtime/vmStructs.cpp
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java
src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java
test/hotspot/jtreg/serviceability/sa/ClhsdbField.java
test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java
test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java
--- a/src/hotspot/share/runtime/thread.cpp	Tue May 21 00:52:04 2019 -0700
+++ b/src/hotspot/share/runtime/thread.cpp	Tue May 21 10:34:57 2019 +0200
@@ -1613,7 +1613,6 @@
   set_deopt_compiled_method(NULL);
   clear_must_deopt_id();
   set_monitor_chunks(NULL);
-  set_next(NULL);
   _on_thread_list = false;
   set_thread_state(_thread_new);
   _terminated = _not_terminated;
@@ -3457,7 +3456,6 @@
 // would like. We are actively migrating Threads_lock uses to other
 // mechanisms in order to reduce Threads_lock contention.
 
-JavaThread* Threads::_thread_list = NULL;
 int         Threads::_number_of_threads = 0;
 int         Threads::_number_of_non_daemon_threads = 0;
 int         Threads::_return_code = 0;
@@ -3764,7 +3762,6 @@
   }
 
   // Initialize Threads state
-  _thread_list = NULL;
   _number_of_threads = 0;
   _number_of_non_daemon_threads = 0;
 
@@ -4423,9 +4420,6 @@
 
   BarrierSet::barrier_set()->on_thread_attach(p);
 
-  p->set_next(_thread_list);
-  _thread_list = p;
-
   // Once a JavaThread is added to the Threads list, smr_delete() has
   // to be used to delete it. Otherwise we can just delete it directly.
   p->set_on_thread_list();
@@ -4463,20 +4457,6 @@
     // Maintain fast thread list
     ThreadsSMRSupport::remove_thread(p);
 
-    JavaThread* current = _thread_list;
-    JavaThread* prev    = NULL;
-
-    while (current != p) {
-      prev    = current;
-      current = current->next();
-    }
-
-    if (prev) {
-      prev->set_next(current->next());
-    } else {
-      _thread_list = p->next();
-    }
-
     _number_of_threads--;
     if (!is_daemon) {
       _number_of_non_daemon_threads--;
--- a/src/hotspot/share/runtime/thread.hpp	Tue May 21 00:52:04 2019 -0700
+++ b/src/hotspot/share/runtime/thread.hpp	Tue May 21 10:34:57 2019 +0200
@@ -983,7 +983,6 @@
   friend class JVMCIVMStructs;
   friend class WhiteBox;
  private:
-  JavaThread*    _next;                          // The next thread in the Threads list
   bool           _on_thread_list;                // Is set when this JavaThread is added to the Threads list
   oop            _threadObj;                     // The Java level thread object
 
@@ -1247,10 +1246,6 @@
   virtual bool is_Java_thread() const            { return true;  }
   virtual bool can_call_java() const             { return true; }
 
-  // Thread chain operations
-  JavaThread* next() const                       { return _next; }
-  void set_next(JavaThread* p)                   { _next = p; }
-
   // Thread oop. threadObj() can be NULL for initial JavaThread
   // (or for threads attached via JNI)
   oop threadObj() const                          { return _threadObj; }
@@ -2213,7 +2208,6 @@
 class Threads: AllStatic {
   friend class VMStructs;
  private:
-  static JavaThread* _thread_list;
   static int         _number_of_threads;
   static int         _number_of_non_daemon_threads;
   static int         _return_code;
--- a/src/hotspot/share/runtime/threadSMR.hpp	Tue May 21 00:52:04 2019 -0700
+++ b/src/hotspot/share/runtime/threadSMR.hpp	Tue May 21 10:34:57 2019 +0200
@@ -86,6 +86,7 @@
 // SMR Support for the Threads class.
 //
 class ThreadsSMRSupport : AllStatic {
+  friend class VMStructs;
   friend class SafeThreadsListPtr;  // for _nested_thread_list_max, delete_notify(), release_stable_list_wake_up() access
 
   // The coordination between ThreadsSMRSupport::release_stable_list() and
@@ -158,6 +159,7 @@
 // A fast list of JavaThreads.
 //
 class ThreadsList : public CHeapObj<mtThread> {
+  friend class VMStructs;
   friend class SafeThreadsListPtr;  // for {dec,inc}_nested_handle_cnt() access
   friend class ThreadsSMRSupport;  // for _nested_handle_cnt, {add,remove}_thread(), {,set_}next_list() access
 
--- a/src/hotspot/share/runtime/vmStructs.cpp	Tue May 21 00:52:04 2019 -0700
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Tue May 21 10:34:57 2019 +0200
@@ -94,6 +94,7 @@
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vmStructs.hpp"
 #include "utilities/globalDefinitions.hpp"
@@ -740,10 +741,13 @@
   /* Threads (NOTE: incomplete) */                                                                                                   \
   /******************************/                                                                                                   \
                                                                                                                                      \
-     static_field(Threads,                     _thread_list,                                  JavaThread*)                           \
-     static_field(Threads,                     _number_of_threads,                            int)                                   \
-     static_field(Threads,                     _number_of_non_daemon_threads,                 int)                                   \
-     static_field(Threads,                     _return_code,                                  int)                                   \
+  static_field(Threads,                     _number_of_threads,                               int)                                   \
+  static_field(Threads,                     _number_of_non_daemon_threads,                    int)                                   \
+  static_field(Threads,                     _return_code,                                     int)                                   \
+                                                                                                                                     \
+  static_ptr_volatile_field(ThreadsSMRSupport, _java_thread_list,                             ThreadsList*)                          \
+  nonstatic_field(ThreadsList,                 _length,                                       const uint)                            \
+  nonstatic_field(ThreadsList,                 _threads,                                      JavaThread *const *const)              \
                                                                                                                                      \
   nonstatic_field(ThreadShadow,                _pending_exception,                            oop)                                   \
   nonstatic_field(ThreadShadow,                _exception_file,                               const char*)                           \
@@ -757,7 +761,6 @@
   nonstatic_field(Thread,                      _current_waiting_monitor,                      ObjectMonitor*)                        \
   nonstatic_field(NamedThread,                 _name,                                         char*)                                 \
   nonstatic_field(NamedThread,                 _processed_thread,                             JavaThread*)                           \
-  nonstatic_field(JavaThread,                  _next,                                         JavaThread*)                           \
   nonstatic_field(JavaThread,                  _threadObj,                                    oop)                                   \
   nonstatic_field(JavaThread,                  _anchor,                                       JavaFrameAnchor)                       \
   nonstatic_field(JavaThread,                  _vm_result,                                    oop)                                   \
@@ -1371,6 +1374,9 @@
   declare_toplevel_type(OSThread)                                         \
   declare_toplevel_type(JavaFrameAnchor)                                  \
                                                                           \
+  declare_toplevel_type(ThreadsSMRSupport)                                \
+  declare_toplevel_type(ThreadsList)                                      \
+                                                                          \
   /***************/                                                       \
   /* Interpreter */                                                       \
   /***************/                                                       \
@@ -1964,6 +1970,7 @@
   declare_toplevel_type(intptr_t*)                                        \
    declare_unsigned_integer_type(InvocationCounter) /* FIXME: wrong type (not integer) */ \
   declare_toplevel_type(JavaThread*)                                      \
+  declare_toplevel_type(JavaThread *const *const)                         \
   declare_toplevel_type(java_lang_Class)                                  \
   declare_integer_type(JavaThread::AsyncRequests)                         \
   declare_integer_type(JavaThread::TerminatedTypes)                       \
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java	Tue May 21 10:34:57 2019 +0200
@@ -536,7 +536,8 @@
                 // Not an address
                 boolean all = name.equals("-a");
                 Threads threads = VM.getVM().getThreads();
-                for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                    JavaThread thread = threads.getJavaThreadAt(i);
                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
                     thread.printThreadIDOn(new PrintStream(bos));
                     if (all || bos.toString().equals(name)) {
@@ -898,7 +899,8 @@
                     String name = t.nextToken();
                     boolean all = name.equals("-a");
                     Threads threads = VM.getVM().getThreads();
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
                         thread.printThreadIDOn(new PrintStream(bos));
                         if (all || bos.toString().equals(name)) {
@@ -927,7 +929,8 @@
                     String name = t.nextToken();
                     boolean all = name.equals("-a");
                     Threads threads = VM.getVM().getThreads();
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
                         thread.printThreadIDOn(new PrintStream(bos));
                         if (all || bos.toString().equals(name)) {
@@ -954,7 +957,8 @@
                     String name = t.nextToken();
                     boolean all = name.equals("-a");
                     Threads threads = VM.getVM().getThreads();
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
                         thread.printThreadIDOn(new PrintStream(bos));
                         if (all || bos.toString().equals(name)) {
@@ -1437,7 +1441,8 @@
                 final long stride = VM.getVM().getAddressSize();
                 if (type.equals("threads")) {
                     Threads threads = VM.getVM().getThreads();
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         Address base = thread.getStackBase();
                         Address end = thread.getLastJavaSP();
                         if (end == null) continue;
@@ -1561,7 +1566,8 @@
                     String name = t.nextToken();
                     Threads threads = VM.getVM().getThreads();
                     boolean all = name.equals("-a");
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
                         thread.printThreadIDOn(new PrintStream(bos));
                         if (all || bos.toString().equals(name)) {
@@ -1590,7 +1596,8 @@
                     String name = t.nextToken();
                     Threads threads = VM.getVM().getThreads();
                     boolean all = name.equals("-a");
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         ByteArrayOutputStream bos = new ByteArrayOutputStream();
                         thread.printThreadIDOn(new PrintStream(bos));
                         if (all || bos.toString().equals(name)) {
@@ -1613,7 +1620,8 @@
                     usage();
                 } else {
                     Threads threads = VM.getVM().getThreads();
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         thread.printThreadIDOn(out);
                         out.println(" " + thread.getThreadName());
                         thread.printInfoOn(out);
@@ -1631,7 +1639,8 @@
                     ArrayList nmethods = new ArrayList();
                     Threads threads = VM.getVM().getThreads();
                     HTMLGenerator gen = new HTMLGenerator(false);
-                    for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+                    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                        JavaThread thread = threads.getJavaThreadAt(i);
                         try {
                             for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
                                 if (vf instanceof CompiledVFrame) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -619,10 +619,10 @@
         Threads threads = VM.getVM().getThreads();
         int len = threads.getNumberOfThreads();
         long[] result = new long[len * 3];    // triple
-        JavaThread t = threads.first();
         long beg, end;
         int i = 0;
-        while (t != null) {
+        for (int k = 0; k < threads.getNumberOfThreads(); k++) {
+            JavaThread t = threads.getJavaThreadAt(k);
             end = t.getStackBaseValue();
             beg = end - t.getStackSize();
             BsdThread bsdt = (BsdThread)t.getThreadProxy();
@@ -631,7 +631,6 @@
             result[i] = uid;
             result[i + 1] = beg;
             result[i + 2] = end;
-            t = t.next();
             i += 3;
         }
         return result;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java	Tue May 21 10:34:57 2019 +0200
@@ -357,7 +357,9 @@
     // end.
 
     if (VM.getVM().getUseTLAB()) {
-      for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) {
+      Threads threads = VM.getVM().getThreads();
+      for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+        JavaThread thread = threads.getJavaThreadAt(i);
         ThreadLocalAllocBuffer tlab = thread.tlab();
         if (tlab.start() != null) {
           if ((tlab.top() == null) || (tlab.end() == null)) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/DeadlockDetector.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -146,7 +146,9 @@
 
     private static void createThreadTable() {
         threadTable = new HashMap();
-        for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) {
+        Threads threads = VM.getVM().getThreads();
+        for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+            JavaThread cur = threads.getJavaThreadAt(i);
             // initialize dfn for each thread to -1
             threadTable.put(cur, new Integer(-1));
         }
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -41,7 +41,6 @@
 public class JavaThread extends Thread {
   private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.JavaThread.DEBUG") != null;
 
-  private static AddressField  nextField;
   private static sun.jvm.hotspot.types.OopField threadObjField;
   private static AddressField  anchorField;
   private static AddressField  lastJavaSPField;
@@ -84,7 +83,6 @@
     Type type = db.lookupType("JavaThread");
     Type anchorType = db.lookupType("JavaFrameAnchor");
 
-    nextField         = type.getAddressField("_next");
     threadObjField    = type.getOopField("_threadObj");
     anchorField       = type.getAddressField("_anchor");
     lastJavaSPField   = anchorType.getAddressField("_last_Java_sp");
@@ -120,15 +118,6 @@
     this.access = access;
   }
 
-  public JavaThread next() {
-    Address threadAddr = nextField.getValue(addr);
-    if (threadAddr == null) {
-      return null;
-    }
-
-    return VM.getVM().getThreads().createJavaThreadWrapper(threadAddr);
-  }
-
   /** NOTE: for convenience, this differs in definition from the underlying VM.
       Only "pure" JavaThreads return true; CompilerThreads, the CodeCacheSweeperThread,
       JVMDIDebuggerThreads return false.
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,12 +42,41 @@
 import sun.jvm.hotspot.runtime.bsd_amd64.BsdAMD64JavaThreadPDAccess;
 import sun.jvm.hotspot.utilities.*;
 
+class ThreadsList extends VMObject {
+    private static AddressField  threadsField;
+    private static CIntegerField lengthField;
+
+    static {
+        VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase()));
+    }
+
+    private static synchronized void initialize(TypeDataBase db) {
+        Type type = db.lookupType("ThreadsList");
+        lengthField = type.getCIntegerField("_length");
+        threadsField = type.getAddressField("_threads");
+    }
+
+    public Address getJavaThreadAddressAt(int i) {
+      Address threadAddr = threadsField.getValue(addr);
+      Address at = threadAddr.getAddressAt(VM.getVM().getAddressSize() * i);
+      return at;
+    }
+
+    public long length() {
+        return lengthField.getValue(addr);
+    }
+
+    public ThreadsList(Address addr) {
+        super(addr);
+    }
+}
+
 public class Threads {
     private static JavaThreadFactory threadFactory;
     private static AddressField      threadListField;
-    private static CIntegerField     numOfThreadsField;
     private static VirtualConstructor virtualConstructor;
     private static JavaThreadPDAccess access;
+    private static ThreadsList _list;
 
     static {
         VM.registerVMInitializedObserver(new Observer() {
@@ -58,10 +87,8 @@
     }
 
     private static synchronized void initialize(TypeDataBase db) {
-        Type type = db.lookupType("Threads");
-
-        threadListField = type.getAddressField("_thread_list");
-        numOfThreadsField = type.getCIntegerField("_number_of_threads");
+        Type type = db.lookupType("ThreadsSMRSupport");
+        threadListField = type.getAddressField("_java_thread_list");
 
         // Instantiate appropriate platform-specific JavaThreadFactory
         String os  = VM.getVM().getOS();
@@ -134,6 +161,7 @@
     }
 
     public Threads() {
+        _list = VMObjectFactory.newObject(ThreadsList.class, threadListField.getValue());
     }
 
     /** NOTE: this returns objects of type JavaThread, CompilerThread,
@@ -147,17 +175,15 @@
       false for the three subclasses. FIXME: should reconsider the
       inheritance hierarchy; see {@link
       sun.jvm.hotspot.runtime.JavaThread#isJavaThread}. */
-    public JavaThread first() {
-        Address threadAddr = threadListField.getValue();
-        if (threadAddr == null) {
-            return null;
+    public JavaThread getJavaThreadAt(int i) {
+        if (i < _list.length()) {
+            return createJavaThreadWrapper(_list.getJavaThreadAddressAt(i));
         }
-
-        return createJavaThreadWrapper(threadAddr);
+        return null;
     }
 
     public int getNumberOfThreads() {
-        return (int) numOfThreadsField.getValue();
+        return (int) _list.length();
     }
 
     /** Routine for instantiating appropriately-typed wrapper for a
@@ -177,7 +203,9 @@
     /** Memory operations */
     public void oopsDo(AddressVisitor oopVisitor) {
         // FIXME: add more of VM functionality
-        for (JavaThread thread = first(); thread != null; thread = thread.next()) {
+        Threads threads = VM.getVM().getThreads();
+        for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+            JavaThread thread = threads.getJavaThreadAt(i);
             thread.oopsDo(oopVisitor);
         }
     }
@@ -185,15 +213,17 @@
     // refer to Threads::owning_thread_from_monitor_owner
     public JavaThread owningThreadFromMonitor(Address o) {
         if (o == null) return null;
-        for (JavaThread thread = first(); thread != null; thread = thread.next()) {
+        for (int i = 0; i < getNumberOfThreads(); i++) {
+            JavaThread thread = getJavaThreadAt(i);
             if (o.equals(thread.threadObjectAddress())) {
                 return thread;
             }
         }
 
-        for (JavaThread thread = first(); thread != null; thread = thread.next()) {
-          if (thread.isLockOwned(o))
-            return thread;
+        for (int i = 0; i < getNumberOfThreads(); i++) {
+            JavaThread thread = getJavaThreadAt(i);
+            if (thread.isLockOwned(o))
+                return thread;
         }
         return null;
     }
@@ -206,7 +236,8 @@
     // Get list of Java threads that are waiting to enter the specified monitor.
     public List getPendingThreads(ObjectMonitor monitor) {
         List pendingThreads = new ArrayList();
-        for (JavaThread thread = first(); thread != null; thread = thread.next()) {
+        for (int i = 0; i < getNumberOfThreads(); i++) {
+            JavaThread thread = getJavaThreadAt(i);
             if (thread.isCompilerThread() || thread.isCodeCacheSweeperThread()) {
                 continue;
             }
@@ -221,7 +252,8 @@
     // Get list of Java threads that have called Object.wait on the specified monitor.
     public List getWaitingThreads(ObjectMonitor monitor) {
         List pendingThreads = new ArrayList();
-        for (JavaThread thread = first(); thread != null; thread = thread.next()) {
+        for (int i = 0; i < getNumberOfThreads(); i++) {
+            JavaThread thread = getJavaThreadAt(i);
             ObjectMonitor waiting = thread.getCurrentWaitingMonitor();
             if (monitor.equals(waiting)) {
                 pendingThreads.add(thread);
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -201,7 +201,8 @@
       jframeCache = new HashMap();
       proxyToThread = new HashMap();
       Threads threads = VM.getVM().getThreads();
-      for (JavaThread cur = threads.first(); cur != null; cur = cur.next()) {
+      for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+         JavaThread cur = threads.getJavaThreadAt(i);
          List tmp = new ArrayList(10);
          try {
             for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -71,8 +71,8 @@
                 concLocksPrinter = new ConcurrentLocksPrinter();
             }
             Threads threads = VM.getVM().getThreads();
-            int i = 1;
-            for (JavaThread cur = threads.first(); cur != null; cur = cur.next(), i++) {
+            for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                JavaThread cur = threads.getJavaThreadAt(i);
                 if (cur.isJavaThread()) {
                     cur.printThreadInfoOn(tty);
                     try {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/JavaThreadsPanel.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -459,12 +459,13 @@
     }
 
     private void cache() {
-        Threads threads = VM.getVM().getThreads();
-        for (JavaThread t = threads.first(); t != null; t = t.next()) {
-            if (t.isJavaThread()) {
-                cachedThreads.add(new CachedThread(t));
-            }
+      Threads threads = VM.getVM().getThreads();
+      for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+        JavaThread t = threads.getJavaThreadAt(i);
+        if (t.isJavaThread()) {
+            cachedThreads.add(new CachedThread(t));
         }
+      }
     }
 
     private void decache() {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/AbstractHeapGraphWriter.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -129,15 +129,12 @@
 
     protected void writeJavaThreads() throws IOException {
         Threads threads = VM.getVM().getThreads();
-        JavaThread jt = threads.first();
-        int index = 1;
-        while (jt != null) {
+        for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+            JavaThread jt = threads.getJavaThreadAt(i);
             if (jt.getThreadObj() != null) {
                 // Note that the thread serial number range is 1-to-N
-                writeJavaThread(jt, index);
-                index++;
+                writeJavaThread(jt, i + 1);
             }
-            jt = jt.next();
         }
     }
 
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/HeapHprofBinWriter.java	Tue May 21 10:34:57 2019 +0200
@@ -708,8 +708,8 @@
         int frameSerialNum = 0;
         int numThreads = 0;
         Threads threads = VM.getVM().getThreads();
-
-        for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) {
+        for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+            JavaThread thread = threads.getJavaThreadAt(i);
             Oop threadObj = thread.getThreadObj();
             if (threadObj != null && !thread.isExiting() && !thread.isHiddenFromExternalView()) {
 
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/PointerFinder.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -56,7 +56,9 @@
 
         if (VM.getVM().getUseTLAB()) {
           // Try to find thread containing it
-          for (JavaThread t = VM.getVM().getThreads().first(); t != null; t = t.next()) {
+          Threads threads = VM.getVM().getThreads();
+          for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+            JavaThread t = threads.getJavaThreadAt(i);
             ThreadLocalAllocBuffer tlab = t.tlab();
             if (tlab.contains(a)) {
               loc.inTLAB = true;
@@ -125,7 +127,9 @@
       return loc;
     }
     // Look in thread-local handles
-    for (JavaThread t = VM.getVM().getThreads().first(); t != null; t = t.next()) {
+    Threads threads = VM.getVM().getThreads();
+    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+      JavaThread t = threads.getJavaThreadAt(i);
       JNIHandleBlock handleBlock = t.activeHandles();
       if (handleBlock != null) {
         handleBlock = handleBlock.blockContainingHandle(a);
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ReversePtrsAnalysis.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -92,9 +92,9 @@
     heap = vm.getObjectHeap();
 
     // Do each thread's roots
-    for (JavaThread thread = VM.getVM().getThreads().first();
-         thread != null;
-         thread = thread.next()) {
+    Threads threads = VM.getVM().getThreads();
+    for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+      JavaThread thread = threads.getJavaThreadAt(i);
       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       thread.printThreadIDOn(new PrintStream(bos));
       String threadDesc =
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java	Tue May 21 00:52:04 2019 -0700
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/soql/JSJavaVM.java	Tue May 21 10:34:57 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -189,12 +189,12 @@
 
     private synchronized JSList getThreads() {
         if (threadsCache == null) {
-            List threads = new ArrayList(0);
-            threadsCache = factory.newJSList(threads);
-            JavaThread jthread = vm.getThreads().first();
-            while (jthread != null) {
-                threads.add(jthread);
-                jthread = jthread.next();
+            List threadsList = new ArrayList(0);
+            threadsCache = factory.newJSList(threadsList);
+            Threads threads = VM.getVM().getThreads();
+            for (int i = 0; i < threads.getNumberOfThreads(); i++) {
+                JavaThread thread = threads.getJavaThreadAt(i);
+                threadsList.add(thread);
             }
         }
         return threadsCache;
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java	Tue May 21 00:52:04 2019 -0700
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java	Tue May 21 10:34:57 2019 +0200
@@ -55,7 +55,6 @@
                 "field InstanceKlass _methods Array<Method*>*",
                 "field InstanceKlass _constants ConstantPool*",
                 "field Klass _name Symbol*",
-                "field JavaThread _next JavaThread*",
                 "field JavaThread _osthread OSThread*",
                 "field JVMState _bci",
                 "field TenuredGeneration _the_space ContiguousSpace*",
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java	Tue May 21 00:52:04 2019 -0700
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java	Tue May 21 10:34:57 2019 +0200
@@ -68,8 +68,7 @@
                     "SystemDictionary::Object_klass_knum"));
             expStrMap.put("printstatics Threads", List.of(
                     "Static fields of Threads",
-                    "_number_of_threads", "_number_of_non_daemon_threads",
-                    "JavaThread\\* Threads"));
+                    "_number_of_threads", "_number_of_non_daemon_threads"));
             expStrMap.put("printstatics Universe", List.of(
                     "Static fields of Universe",
                     "uintptr_t Universe::_verify_oop_mask",
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java	Tue May 21 00:52:04 2019 -0700
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java	Tue May 21 10:34:57 2019 +0200
@@ -57,7 +57,6 @@
                 "field Klass _name Symbol*",
                 "type ClassLoaderData* null",
                 "type DictionaryEntry KlassHashtableEntry",
-                "field JavaThread _next JavaThread*",
                 "field JavaThread _osthread OSThread*",
                 "type TenuredGeneration CardGeneration",
                 "field JVMState _bci",