diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp
index 5f2d2988a97..caf14ce1164 100644
--- a/src/hotspot/share/runtime/mutex.cpp
+++ b/src/hotspot/share/runtime/mutex.cpp
@@ -31,7 +31,6 @@
 #include "runtime/osThread.hpp"
 #include "runtime/safepointMechanism.inline.hpp"
 #include "runtime/thread.inline.hpp"
-#include "runtime/threadCritical.hpp"
 #include "utilities/events.hpp"
 #include "utilities/macros.hpp"
 
@@ -268,36 +267,7 @@ bool Monitor::wait(int64_t timeout) {
   return wait_status != 0;          // return true IFF timeout
 }
 
-// Array used to print owned locks on error.
-static Mutex* _mutex_array = NULL;
-
-void Mutex::add_to_global_list() {
-  // Add mutex to print_owned_locks_on_error array
-  ThreadCritical tc;
-  Mutex* next = _mutex_array;
-  _next_mutex = next;
-  _prev_mutex = nullptr;
-  _mutex_array = this;
-  if (next != nullptr) {
-    next->_prev_mutex = this;
-  }
-}
-
-void Mutex::remove_from_global_list() {
-  // Remove mutex from print_owned_locks_on_error array
-  ThreadCritical tc;
-  Mutex* old_next = _next_mutex;
-  assert(old_next != nullptr, "this list can never be empty");
-  old_next->_prev_mutex = _prev_mutex;
-  if (_prev_mutex == nullptr) {
-    _mutex_array = old_next;
-  } else {
-    _prev_mutex->_next_mutex = old_next;
-  }
-}
-
 Mutex::~Mutex() {
-  remove_from_global_list();
   assert_owner(NULL);
   os::free(const_cast<char*>(_name));
 }
@@ -310,8 +280,6 @@ Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(NULL) {
   _allow_vm_block  = allow_vm_block;
   _rank            = rank;
   _skip_rank_check = false;
-  _next            = nullptr;
-  _last_owner      = nullptr;
 
   assert(_rank >= static_cast<Rank>(0) && _rank <= safepoint, "Bad lock rank %s: %s", rank_name(), name);
 
@@ -320,51 +288,16 @@ Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(NULL) {
   assert(_rank > nosafepoint || _allow_vm_block,
          "Locks that don't check for safepoint should always allow the vm to block: %s", name);
 #endif
-  add_to_global_list();
 }
 
 bool Mutex::owned_by_self() const {
   return owner() == Thread::current();
 }
 
-void Mutex::print_on(outputStream* st) const {
+void Mutex::print_on_error(outputStream* st) const {
   st->print("[" PTR_FORMAT, p2i(this));
   st->print("] %s", _name);
   st->print(" - owner thread: " PTR_FORMAT, p2i(owner()));
-#ifdef ASSERT
-  if (_allow_vm_block) {
-    st->print_raw(" allow_vm_block");
-  }
-  st->print(" %s", rank_name());
-#endif
-  st->cr();
-}
-
-// Print all mutexes/monitors that are currently owned by a thread; called
-// by fatal error handler.
-// This function doesn't take the ThreadCritical lock to avoid potential
-// deadlock during error reporting.
-void Mutex::print_owned_locks_on_error(outputStream* st) {
-  assert(VMError::is_error_reported(), "should only be called during error reporting");
-  ResourceMark rm;
-  st->print("VM Mutexes/Monitors currently owned by a thread: ");
-  bool none = true;
-  Mutex *m = _mutex_array;
-  int array_count = 0;
-  while (m != nullptr) {
-     array_count++;
-     // see if it has an owner
-     if (m->owner() != NULL) {
-       if (none) {
-          st->cr();
-          none = false;
-       }
-       m->print_on(st);
-     }
-     m = m->_next_mutex;
-  }
-  if (none) st->print_cr("None");
-  st->print_cr("Total Mutex count %d", array_count);
 }
 
 // ----------------------------------------------------------------------------------
@@ -410,7 +343,21 @@ void Mutex::assert_no_overlap(Rank orig, Rank adjusted, int adjust) {
            rank_name_internal(orig), adjust, rank_name_internal(adjusted));
   }
 }
+#endif // ASSERT
 
+#ifndef PRODUCT
+void Mutex::print_on(outputStream* st) const {
+  st->print("Mutex: [" PTR_FORMAT "] %s - owner: " PTR_FORMAT,
+            p2i(this), _name, p2i(owner()));
+  if (_allow_vm_block) {
+    st->print("%s", " allow_vm_block");
+  }
+  DEBUG_ONLY(st->print(" %s", rank_name()));
+  st->cr();
+}
+#endif // PRODUCT
+
+#ifdef ASSERT
 void Mutex::assert_owner(Thread * expected) {
   const char* msg = "invalid owner";
   if (expected == NULL) {
diff --git a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp
index 70b17a7d552..586b69f1082 100644
--- a/src/hotspot/share/runtime/mutex.hpp
+++ b/src/hotspot/share/runtime/mutex.hpp
@@ -88,24 +88,19 @@ class Mutex : public CHeapObj<mtSynchronizer> {
   Thread* volatile _owner;
   void raw_set_owner(Thread* new_owner) { Atomic::store(&_owner, new_owner); }
 
-  // Embed pointers for mutex array for error reporting.
-  Mutex* _next_mutex;
-  Mutex* _prev_mutex;
-
-  void add_to_global_list();
-  void remove_from_global_list();
-
  protected:                              // Monitor-Mutex metadata
   os::PlatformMonitor _lock;             // Native monitor implementation
   const char* _name;                     // Name of mutex/monitor
 
-  // Debugging fields for naming, deadlock detection, etc.
+  // Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
+#ifndef PRODUCT
+  bool    _allow_vm_block;
+#endif
 #ifdef ASSERT
   Rank    _rank;                 // rank (to avoid/detect potential deadlocks)
   Mutex*  _next;                 // Used by a Thread to link up owned locks
   Thread* _last_owner;           // the last thread to own the lock
-  bool    _skip_rank_check;      // read only by owner when doing rank checks
-  bool    _allow_vm_block;
+  bool _skip_rank_check;         // read only by owner when doing rank checks
 
   static bool contains(Mutex* locks, Mutex* lock);
   static Mutex* get_least_ranked_lock(Mutex* locks);
@@ -194,11 +189,11 @@ class Mutex : public CHeapObj<mtSynchronizer> {
 
   const char *name() const                  { return _name; }
 
-  // Print all mutexes/monitors that are currently owned by a thread; called
-  // by fatal error handler.
-  static void print_owned_locks_on_error(outputStream* st);
-  void print_on(outputStream* st) const;
-  void print() const { print_on(::tty); }
+  void print_on_error(outputStream* st) const;
+  #ifndef PRODUCT
+    void print_on(outputStream* st) const;
+    void print() const                      { print_on(::tty); }
+  #endif
 };
 
 class Monitor : public Mutex {
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
index 227ad386817..35900865d37 100644
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -157,6 +157,11 @@ Mutex*   Bootclasspath_lock           = NULL;
 Monitor* JVMCI_lock                   = NULL;
 #endif
 
+
+#define MAX_NUM_MUTEX 128
+static Mutex* _mutex_array[MAX_NUM_MUTEX];
+static int _num_mutex;
+
 #ifdef ASSERT
 void assert_locked_or_safepoint(const Mutex* lock) {
   // check if this thread owns the lock (common case)
@@ -189,18 +194,26 @@ void assert_locked_or_safepoint_or_handshake(const Mutex* lock, const JavaThread
 }
 #endif
 
+static void add_mutex(Mutex* var) {
+  assert(_num_mutex < MAX_NUM_MUTEX, "increase MAX_NUM_MUTEX");
+  _mutex_array[_num_mutex++] = var;
+}
+
 #define def(var, type, pri, vm_block) {       \
   var = new type(Mutex::pri, #var, vm_block); \
+  add_mutex(var);                             \
 }
 
 // Specify relative ranked lock
 #ifdef ASSERT
 #define defl(var, type, held_lock, vm_block) {         \
   var = new type(held_lock->rank()-1, #var, vm_block); \
+  add_mutex(var);                                      \
 }
 #else
 #define defl(var, type, held_lock, vm_block) {         \
   var = new type(Mutex::safepoint, #var, vm_block);    \
+  add_mutex(var);                                      \
 }
 #endif
 
@@ -364,3 +377,23 @@ GCMutexLocker::GCMutexLocker(Mutex* mutex) {
     _mutex->lock();
   }
 }
+
+// Print all mutexes/monitors that are currently owned by a thread; called
+// by fatal error handler.
+void print_owned_locks_on_error(outputStream* st) {
+  st->print("VM Mutex/Monitor currently owned by a thread: ");
+  bool none = true;
+  for (int i = 0; i < _num_mutex; i++) {
+     // see if it has an owner
+     if (_mutex_array[i]->owner() != NULL) {
+       if (none) {
+          // print format used by Mutex::print_on_error()
+          st->print_cr(" ([mutex/lock_event])");
+          none = false;
+       }
+       _mutex_array[i]->print_on_error(st);
+       st->cr();
+     }
+  }
+  if (none) st->print_cr("None");
+}
diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
index fa6671bb0e1..c4c72150060 100644
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -169,6 +169,12 @@ extern Mutex* tty_lock;                          // lock to synchronize output.
 // order.  If their implementations change such that these assumptions
 // are violated, a whole lot of code will break.
 
+// Print all mutexes/monitors that are currently owned by a thread; called
+// by fatal error handler.
+void print_owned_locks_on_error(outputStream* st);
+
+char *lock_name(Mutex *mutex);
+
 // for debugging: check that we're already owning this lock (or are at a safepoint / handshake)
 #ifdef ASSERT
 void assert_locked_or_safepoint(const Mutex* lock);
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index 44c7376dc1e..d6152164dbb 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -1015,7 +1015,7 @@ void VMError::report(outputStream* st, bool _verbose) {
 
      // mutexes/monitors that currently have an owner
      if (_verbose) {
-       Mutex::print_owned_locks_on_error(st);
+       print_owned_locks_on_error(st);
        st->cr();
      }
 
@@ -1913,12 +1913,6 @@ void VMError::controlled_crash(int how) {
   switch (how) {
     case 1: assert(how == 0, "test assert"); break;
     case 2: guarantee(how == 0, "test guarantee"); break;
-    case 3: {
-      Mutex* ErrorTest_lock = new Mutex(Mutex::nosafepoint, "ErrorTest_lock");
-      MutexLocker ml(ErrorTest_lock, Mutex::_no_safepoint_check_flag);
-      assert(how == 0, "test assert with lock");
-      break;
-    }
 
     // The other cases are unused.
     case 14: crash_with_segfault(); break;
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileOverwriteTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileOverwriteTest.java
index 9108213722c..c9b9e433e18 100644
--- a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileOverwriteTest.java
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileOverwriteTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2019, SAP. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -42,6 +42,46 @@ import java.util.regex.Pattern;
 
 public class ErrorFileOverwriteTest {
 
+  private static File findHsErrorFileInOutput(OutputAnalyzer output) {
+
+    String hs_err_file = output.firstMatch("# *(\\S*hs_err_pid.*\\.log)", 1);
+    if(hs_err_file ==null) {
+      throw new RuntimeException("Did not find hs-err file in output.\n");
+    }
+
+    File f = new File(hs_err_file);
+    if (!f.exists()) {
+      throw new RuntimeException("hs-err file missing at "
+              + f.getAbsolutePath() + ".\n");
+    }
+
+    return f;
+
+  }
+
+  private static void scanHsErrorFileForContent(File f, Pattern[] pattern) throws IOException {
+    FileInputStream fis = new FileInputStream(f);
+    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+    String line = null;
+
+    int currentPattern = 0;
+
+    String lastLine = null;
+    while ((line = br.readLine()) != null && currentPattern < pattern.length) {
+      if (pattern[currentPattern].matcher(line).matches()) {
+        System.out.println("Found: " + line + ".");
+        currentPattern++;
+      }
+      lastLine = line;
+    }
+    br.close();
+
+    if (currentPattern < pattern.length) {
+      throw new RuntimeException("hs-err file incomplete (first missing pattern: " +  pattern[currentPattern] + ")");
+    }
+
+  }
+
   public static void do_test(boolean with_percent_p) throws Exception {
 
     // Crash twice.
@@ -70,10 +110,10 @@ public class ErrorFileOverwriteTest {
     output_detail.shouldMatch("# " + errorFileStem + ".*");
     System.out.println("First crash: Found expected output on tty. Ok.");
 
-    File f = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
+    File f = findHsErrorFileInOutput(output_detail);
     System.out.println("First crash: Found hs error file at " + f.getAbsolutePath());
 
-    ErrorFileScanner.scanHsErrorFileForContent(f, new Pattern[] {
+    scanHsErrorFileForContent(f, new Pattern[] {
             Pattern.compile("# *Internal Error.*"),
             Pattern.compile("Command Line:.*-XX:ErrorHandlerTest=1.*-XX:ErrorFile=" + errorFileStem + ".*")
     });
@@ -96,7 +136,7 @@ public class ErrorFileOverwriteTest {
     output_detail.shouldMatch("# " + errorFileStem + ".*");
     System.out.println("Second crash: Found expected output on tty. Ok.");
 
-    File f2 = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
+    File f2 = findHsErrorFileInOutput(output_detail);
     System.out.println("Second crash: Found hs error file at " + f2.getAbsolutePath());
 
     if (with_percent_p) {
@@ -105,7 +145,7 @@ public class ErrorFileOverwriteTest {
       }
     }
 
-    ErrorFileScanner.scanHsErrorFileForContent(f2, new Pattern[] {
+    scanHsErrorFileForContent(f2, new Pattern[] {
             Pattern.compile("# *Internal Error.*"),
             Pattern.compile("Command Line:.*-XX:ErrorHandlerTest=2.*-XX:ErrorFile=" + errorFileStem + ".*")
     });
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileScanner.java b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileScanner.java
deleted file mode 100644
index 5e36a8b8698..00000000000
--- a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorFileScanner.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2021, 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-
-import java.util.regex.Pattern;
-
-import jdk.test.lib.process.OutputAnalyzer;
-
-public class ErrorFileScanner {
-
-  public static File findHsErrorFileInOutput(OutputAnalyzer output) {
-
-    String hs_err_file = output.firstMatch("# *(\\S*hs_err_pid.*\\.log)", 1);
-    if(hs_err_file ==null) {
-      throw new RuntimeException("Did not find hs-err file in output.\n");
-    }
-
-    File f = new File(hs_err_file);
-    if (!f.exists()) {
-      throw new RuntimeException("hs-err file missing at "
-              + f.getAbsolutePath() + ".\n");
-    }
-
-    return f;
-
-  }
-
-  public static void scanHsErrorFileForContent(File f, Pattern[] pattern) throws IOException {
-    FileInputStream fis = new FileInputStream(f);
-    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
-    String line = null;
-
-    int currentPattern = 0;
-
-    String lastLine = null;
-    while ((line = br.readLine()) != null && currentPattern < pattern.length) {
-      if (pattern[currentPattern].matcher(line).matches()) {
-        System.out.println("Found: " + line + ".");
-        currentPattern++;
-      }
-      lastLine = line;
-    }
-    br.close();
-
-    if (currentPattern < pattern.length) {
-      throw new RuntimeException("hs-err file incomplete (first missing pattern: " +  pattern[currentPattern] + ")");
-    }
-
-  }
-}
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestErrorFileMutex.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestErrorFileMutex.java
deleted file mode 100644
index 97ddb48c06d..00000000000
--- a/test/hotspot/jtreg/runtime/ErrorHandling/TestErrorFileMutex.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2021, 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-/*
- * @test
- * @bug 8274794
- * @summary Test that locks are printed in the Error file.
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * @requires (vm.debug == true)
- * @run driver TestErrorFileMutex
- */
-
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.process.ProcessTools;
-
-import java.io.*;
-import java.util.regex.Pattern;
-
-public class TestErrorFileMutex {
-
-  public static void do_test() throws Exception {
-
-    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
-            "-Xmx64M",
-            "-XX:-CreateCoredumpOnCrash",
-            "-XX:ErrorHandlerTest=3",
-            "-version");
-
-    OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
-    output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
-
-    File f = ErrorFileScanner.findHsErrorFileInOutput(output_detail);
-    System.out.println("Found hs error file at " + f.getAbsolutePath());
-
-    ErrorFileScanner.scanHsErrorFileForContent(f, new Pattern[] {
-            Pattern.compile("# *Internal Error.*"),
-            Pattern.compile(".*VM Mutexes/Monitors currently owned by a thread:.*"),
-            Pattern.compile(".*ErrorTest_lock - owner thread:.*"),
-            Pattern.compile(".*Threads_lock - owner thread:.*")
-    });
-  }
-
-  public static void main(String[] args) throws Exception {
-    do_test();
-  }
-
-}
-
-
-