8184994: Add Dictionary size logging and jcmd

Added dcmd for printing system dictionary like the stringtable and symboltable and making print functions go to outputstream rather than tty

Reviewed-by: shade, hseigel
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index ea30dbe..7961741 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -1136,12 +1136,22 @@
   }
 }
 
-void ClassLoaderDataGraph::print_dictionary(bool details) {
+void ClassLoaderDataGraph::print_dictionary(outputStream* st) {
   FOR_ALL_DICTIONARY(cld) {
-    tty->print("Dictionary for class loader ");
-    cld->print_value();
-    tty->cr();
-    cld->dictionary()->print(details);
+    st->print("Dictionary for ");
+    cld->print_value_on(st);
+    st->cr();
+    cld->dictionary()->print_on(st);
+    st->cr();
+  }
+}
+
+void ClassLoaderDataGraph::print_dictionary_statistics(outputStream* st) {
+  FOR_ALL_DICTIONARY(cld) {
+    ResourceMark rm;
+    stringStream tempst;
+    tempst.print("System Dictionary for %s", cld->loader_name());
+    cld->dictionary()->print_table_statistics(st, tempst.as_string());
   }
 }
 
@@ -1422,7 +1432,7 @@
 
 void ClassLoaderData::print_value_on(outputStream* out) const {
   if (class_loader() == NULL) {
-    out->print("NULL class_loader");
+    out->print("NULL class loader");
   } else {
     out->print("class loader " INTPTR_FORMAT " ", p2i(this));
     class_loader()->print_value_on(out);
@@ -1431,7 +1441,7 @@
 
 void ClassLoaderData::print_on(outputStream* out) const {
   if (class_loader() == NULL) {
-    out->print("NULL class_loader");
+    out->print("NULL class loader");
   } else {
     out->print("class loader " INTPTR_FORMAT " ", p2i(this));
     class_loader()->print_on(out);
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp
index 0d90278..0247085 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp
@@ -125,7 +125,8 @@
   static InstanceKlass* try_get_next_class();
 
   static void verify_dictionary();
-  static void print_dictionary(bool details);
+  static void print_dictionary(outputStream* st);
+  static void print_dictionary_statistics(outputStream* st);
 
   // CMS support.
   static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); }
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp
index 6c1c2a3..f1acadf 100644
--- a/hotspot/src/share/vm/classfile/compactHashtable.cpp
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp
@@ -29,6 +29,7 @@
 #include "memory/metadataFactory.hpp"
 #include "memory/metaspaceShared.hpp"
 #include "prims/jvm.h"
+#include "runtime/vmThread.hpp"
 #include "utilities/numberSeq.hpp"
 #include <sys/stat.h>
 
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.hpp b/hotspot/src/share/vm/classfile/compactHashtable.hpp
index 13c6531..a1c9426 100644
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -25,10 +25,8 @@
 #ifndef SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
 #define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
 
-#include "classfile/stringTable.hpp"
-#include "classfile/symbolTable.hpp"
+#include "oops/array.hpp"
 #include "oops/symbol.hpp"
-#include "services/diagnosticCommand.hpp"
 #include "utilities/hashtable.hpp"
 
 template <class T, class N> class CompactHashtable;
@@ -357,89 +355,4 @@
   static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length);
 };
 
-///////////////////////////////////////////////////////////////////////
-//
-// jcmd command support for symbol table and string table dumping:
-//   VM.symboltable -verbose: for dumping the symbol table
-//   VM.stringtable -verbose: for dumping the string table
-//
-class VM_DumpHashtable : public VM_Operation {
-private:
-  outputStream* _out;
-  int _which;
-  bool _verbose;
-public:
-  enum {
-    DumpSymbols = 1 << 0,
-    DumpStrings = 1 << 1,
-    DumpSysDict = 1 << 2  // not implemented yet
-  };
-  VM_DumpHashtable(outputStream* out, int which, bool verbose) {
-    _out = out;
-    _which = which;
-    _verbose = verbose;
-  }
-
-  virtual VMOp_Type type() const { return VMOp_DumpHashtable; }
-
-  virtual void doit() {
-    switch (_which) {
-    case DumpSymbols:
-      SymbolTable::dump(_out, _verbose);
-      break;
-    case DumpStrings:
-      StringTable::dump(_out, _verbose);
-      break;
-    default:
-      ShouldNotReachHere();
-    }
-  }
-};
-
-class SymboltableDCmd : public DCmdWithParser {
-protected:
-  DCmdArgument<bool> _verbose;
-public:
-  SymboltableDCmd(outputStream* output, bool heap);
-  static const char* name() {
-    return "VM.symboltable";
-  }
-  static const char* description() {
-    return "Dump symbol table.";
-  }
-  static const char* impact() {
-    return "Medium: Depends on Java content.";
-  }
-  static const JavaPermission permission() {
-    JavaPermission p = {"java.lang.management.ManagementPermission",
-                        "monitor", NULL};
-    return p;
-  }
-  static int num_arguments();
-  virtual void execute(DCmdSource source, TRAPS);
-};
-
-class StringtableDCmd : public DCmdWithParser {
-protected:
-  DCmdArgument<bool> _verbose;
-public:
-  StringtableDCmd(outputStream* output, bool heap);
-  static const char* name() {
-    return "VM.stringtable";
-  }
-  static const char* description() {
-    return "Dump string table.";
-  }
-  static const char* impact() {
-    return "Medium: Depends on Java content.";
-  }
-  static const JavaPermission permission() {
-    JavaPermission p = {"java.lang.management.ManagementPermission",
-                        "monitor", NULL};
-    return p;
-  }
-  static int num_arguments();
-  virtual void execute(DCmdSource source, TRAPS);
-};
-
 #endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp
index ffbc797..2491d35 100644
--- a/hotspot/src/share/vm/classfile/dictionary.cpp
+++ b/hotspot/src/share/vm/classfile/dictionary.cpp
@@ -435,16 +435,13 @@
 
 // ----------------------------------------------------------------------------
 
-void Dictionary::print(bool details) {
+void Dictionary::print_on(outputStream* st) const {
   ResourceMark rm;
 
   assert(loader_data() != NULL, "loader data should not be null");
-  if (details) {
-    tty->print_cr("Java dictionary (table_size=%d, classes=%d)",
-                   table_size(), number_of_entries());
-    tty->print_cr("^ indicates that initiating loader is different from "
-                  "defining loader");
-  }
+  st->print_cr("Java dictionary (table_size=%d, classes=%d)",
+               table_size(), number_of_entries());
+  st->print_cr("^ indicates that initiating loader is different from defining loader");
 
   for (int index = 0; index < table_size(); index++) {
     for (DictionaryEntry* probe = bucket(index);
@@ -453,17 +450,15 @@
       Klass* e = probe->instance_klass();
       bool is_defining_class =
          (loader_data() == e->class_loader_data());
-      if (details) {
-        tty->print("%4d: ", index);
+      st->print("%4d: %s%s, loader ", index, is_defining_class ? " " : "^", e->external_name());
+      ClassLoaderData* loader_data = e->class_loader_data();
+      if (loader_data == NULL) {
+        // Shared class not restored yet in shared dictionary
+        st->print("<shared, not restored>");
+      } else {
+        loader_data->print_value_on(st);
       }
-      tty->print("%s%s", ((!details) || is_defining_class) ? " " : "^",
-                 e->external_name());
-
-      if (details) {
-        tty->print(", loader ");
-        e->class_loader_data()->print_value();
-      }
-      tty->cr();
+      st->cr();
     }
   }
   tty->cr();
@@ -493,4 +488,3 @@
   tempst.print("System Dictionary for %s", cld->loader_name());
   verify_table<DictionaryEntry>(tempst.as_string());
 }
-
diff --git a/hotspot/src/share/vm/classfile/dictionary.hpp b/hotspot/src/share/vm/classfile/dictionary.hpp
index 06b4e80..bf9b443 100644
--- a/hotspot/src/share/vm/classfile/dictionary.hpp
+++ b/hotspot/src/share/vm/classfile/dictionary.hpp
@@ -103,10 +103,7 @@
   // Sharing support
   void reorder_dictionary();
 
-  void print(bool details = true);
-#ifdef ASSERT
-  void printPerformanceInfoDetails();
-#endif // ASSERT
+  void print_on(outputStream* st) const;
   void verify();
 };
 
@@ -223,7 +220,7 @@
     return (SymbolPropertyEntry**)HashtableEntry<Symbol*, mtSymbol>::next_addr();
   }
 
-  void print_on(outputStream* st) const {
+  void print_entry(outputStream* st) const {
     symbol()->print_value_on(st);
     st->print("/mode=" INTX_FORMAT, symbol_mode());
     st->print(" -> ");
@@ -306,9 +303,6 @@
   // Sharing support
   void reorder_dictionary();
 
-#ifndef PRODUCT
-  void print();
-#endif
   void verify();
 };
 #endif // SHARE_VM_CLASSFILE_DICTIONARY_HPP
diff --git a/hotspot/src/share/vm/classfile/loaderConstraints.cpp b/hotspot/src/share/vm/classfile/loaderConstraints.cpp
index d7b4837..88a6f31 100644
--- a/hotspot/src/share/vm/classfile/loaderConstraints.cpp
+++ b/hotspot/src/share/vm/classfile/loaderConstraints.cpp
@@ -466,27 +466,24 @@
   }
 }
 
-#ifndef PRODUCT
-
 // Called with the system dictionary lock held
-void LoaderConstraintTable::print() {
+void LoaderConstraintTable::print_on(outputStream* st) const {
   ResourceMark rm;
   assert_locked_or_safepoint(SystemDictionary_lock);
-  tty->print_cr("Java loader constraints (entries=%d, constraints=%d)",
-                table_size(), number_of_entries());
+  st->print_cr("Java loader constraints (table_size=%d, constraints=%d)",
+               table_size(), number_of_entries());
   for (int cindex = 0; cindex < table_size(); cindex++) {
     for (LoaderConstraintEntry* probe = bucket(cindex);
                                 probe != NULL;
                                 probe = probe->next()) {
-      tty->print("%4d: ", cindex);
-      probe->name()->print();
-      tty->print(" , loaders:");
+      st->print("%4d: ", cindex);
+      probe->name()->print_on(st);
+      st->print(" , loaders:");
       for (int n = 0; n < probe->num_loaders(); n++) {
-        probe->loader_data(n)->print_value();
-        tty->print(", ");
+        probe->loader_data(n)->print_value_on(st);
+        st->print(", ");
       }
-      tty->cr();
+      st->cr();
     }
   }
 }
-#endif
diff --git a/hotspot/src/share/vm/classfile/loaderConstraints.hpp b/hotspot/src/share/vm/classfile/loaderConstraints.hpp
index 66554dc..8a7a124 100644
--- a/hotspot/src/share/vm/classfile/loaderConstraints.hpp
+++ b/hotspot/src/share/vm/classfile/loaderConstraints.hpp
@@ -47,7 +47,7 @@
                                    int max_loaders);
   void free_entry(LoaderConstraintEntry *entry);
 
-  LoaderConstraintEntry* bucket(int i) {
+  LoaderConstraintEntry* bucket(int i) const {
     return (LoaderConstraintEntry*)Hashtable<InstanceKlass*, mtClass>::bucket(i);
   }
 
@@ -79,9 +79,7 @@
   void purge_loader_constraints();
 
   void verify(PlaceholderTable* placeholders);
-#ifndef PRODUCT
-  void print();
-#endif
+  void print_on(outputStream* st) const;
 };
 
 class LoaderConstraintEntry : public HashtableEntry<InstanceKlass*, mtClass> {
diff --git a/hotspot/src/share/vm/classfile/placeholders.cpp b/hotspot/src/share/vm/classfile/placeholders.cpp
index db452c9..7fd3c20 100644
--- a/hotspot/src/share/vm/classfile/placeholders.cpp
+++ b/hotspot/src/share/vm/classfile/placeholders.cpp
@@ -175,39 +175,6 @@
     : Hashtable<Symbol*, mtClass>(table_size, sizeof(PlaceholderEntry)) {
 }
 
-#ifndef PRODUCT
-// Note, doesn't append a cr
-void PlaceholderEntry::print() const {
-  klassname()->print_value();
-  if (loader_data() != NULL) {
-    tty->print(", loader ");
-    loader_data()->print_value();
-  }
-  if (supername() != NULL) {
-    tty->print(", supername ");
-    supername()->print_value();
-  }
-  if (definer() != NULL) {
-    tty->print(", definer ");
-    definer()->print_value();
-  }
-  if (instance_klass() != NULL) {
-    tty->print(", InstanceKlass ");
-    instance_klass()->print_value();
-  }
-  tty->print("\n");
-  tty->print("loadInstanceThreadQ threads:");
-  loadInstanceThreadQ()->printActionQ();
-  tty->print("\n");
-  tty->print("superThreadQ threads:");
-  superThreadQ()->printActionQ();
-  tty->print("\n");
-  tty->print("defineThreadQ threads:");
-  defineThreadQ()->printActionQ();
-  tty->print("\n");
-}
-#endif
-
 void PlaceholderEntry::verify() const {
   guarantee(loader_data() != NULL, "Must have been setup.");
   guarantee(loader_data()->class_loader() == NULL || loader_data()->class_loader()->is_instance(),
@@ -222,20 +189,48 @@
 }
 
 
-#ifndef PRODUCT
-void PlaceholderTable::print() {
-  tty->print_cr("Placeholder table table_size=%d, entries=%d",
+// Note, doesn't append a cr
+// Can't call this print_on because HashtableEntry doesn't initialize its vptr
+// and print_on is a virtual function so the vptr call crashes.
+void PlaceholderEntry::print_entry(outputStream* st) const {
+  klassname()->print_value_on(st);
+  if (loader_data() != NULL) {
+    st->print(", loader ");
+    loader_data()->print_value_on(st);
+  }
+  if (supername() != NULL) {
+    st->print(", supername ");
+    supername()->print_value_on(st);
+  }
+  if (definer() != NULL) {
+    st->print(", definer ");
+    definer()->print_value_on(st);
+  }
+  if (instance_klass() != NULL) {
+    st->print(", InstanceKlass ");
+    instance_klass()->print_value_on(st);
+  }
+  st->cr();
+  st->print("loadInstanceThreadQ threads:");
+  loadInstanceThreadQ()->print_action_queue(st);
+  st->cr();
+  st->print("superThreadQ threads:");
+  superThreadQ()->print_action_queue(st);
+  st->cr();
+  st->print("defineThreadQ threads:");
+  defineThreadQ()->print_action_queue(st);
+  st->cr();
+}
+
+void PlaceholderTable::print_on(outputStream* st) const {
+  st->print_cr("Placeholder table (table_size=%d, placeholders=%d)",
                 table_size(), number_of_entries());
   for (int pindex = 0; pindex < table_size(); pindex++) {
     for (PlaceholderEntry* probe = bucket(pindex);
                            probe != NULL;
                            probe = probe->next()) {
-      tty->print("%4d: ", pindex);
-      tty->print(" place holder ");
-
-      probe->print();
-      tty->cr();
+      st->print("%4d: placeholder ", pindex);
+      probe->print_entry(st);
     }
   }
 }
-#endif
diff --git a/hotspot/src/share/vm/classfile/placeholders.hpp b/hotspot/src/share/vm/classfile/placeholders.hpp
index cddfe26..d66db6b 100644
--- a/hotspot/src/share/vm/classfile/placeholders.hpp
+++ b/hotspot/src/share/vm/classfile/placeholders.hpp
@@ -42,7 +42,7 @@
   PlaceholderEntry* new_entry(int hash, Symbol* name, ClassLoaderData* loader_data, bool havesupername, Symbol* supername);
   void free_entry(PlaceholderEntry* entry);
 
-  PlaceholderEntry* bucket(int i) {
+  PlaceholderEntry* bucket(int i) const {
     return (PlaceholderEntry*)Hashtable<Symbol*, mtClass>::bucket(i);
   }
 
@@ -97,9 +97,7 @@
                        Symbol* name, ClassLoaderData* loader_data,
                        classloadAction action, Thread* thread);
 
-#ifndef PRODUCT
-  void print();
-#endif
+  void print_on(outputStream* st) const;
   void verify();
 };
 
@@ -129,16 +127,14 @@
    void set_next(SeenThread *seen) { _stnext = seen; }
    void set_prev(SeenThread *seen) { _stprev = seen; }
 
-#ifndef PRODUCT
-  void printActionQ() {
+  void print_action_queue(outputStream* st) {
     SeenThread* seen = this;
     while (seen != NULL) {
-      seen->thread()->print_value();
-      tty->print(", ");
+      seen->thread()->print_value_on(st);
+      st->print(", ");
       seen = seen->next();
     }
   }
-#endif // PRODUCT
 };
 
 // Placeholder objects represent classes currently being loaded.
@@ -321,7 +317,7 @@
   }
 
   // Print method doesn't append a cr
-  void print() const  PRODUCT_RETURN;
+  void print_entry(outputStream* st) const;
   void verify() const;
 };
 
diff --git a/hotspot/src/share/vm/classfile/protectionDomainCache.cpp b/hotspot/src/share/vm/classfile/protectionDomainCache.cpp
index 25b35ab..d028678 100644
--- a/hotspot/src/share/vm/classfile/protectionDomainCache.cpp
+++ b/hotspot/src/share/vm/classfile/protectionDomainCache.cpp
@@ -80,25 +80,18 @@
   }
 }
 
-#ifndef PRODUCT
-void ProtectionDomainCacheTable::print() {
-  tty->print_cr("Protection domain cache table (table_size=%d, classes=%d)",
-                table_size(), number_of_entries());
+void ProtectionDomainCacheTable::print_on(outputStream* st) const {
+  st->print_cr("Protection domain cache table (table_size=%d, classes=%d)",
+               table_size(), number_of_entries());
   for (int index = 0; index < table_size(); index++) {
     for (ProtectionDomainCacheEntry* probe = bucket(index);
                                      probe != NULL;
                                      probe = probe->next()) {
-      tty->print("%4d: ", index);
-      probe->print();
+      st->print_cr("%4d: protection_domain: " PTR_FORMAT, index, p2i(probe->literal()));
     }
   }
 }
 
-void ProtectionDomainCacheEntry::print() {
-  tty->print_cr("protection_domain: " PTR_FORMAT, p2i(literal()));
-}
-#endif
-
 void ProtectionDomainCacheTable::verify() {
   verify_table<ProtectionDomainCacheEntry>("Protection Domain Table");
 }
diff --git a/hotspot/src/share/vm/classfile/protectionDomainCache.hpp b/hotspot/src/share/vm/classfile/protectionDomainCache.hpp
index 225fcba..36ab23f 100644
--- a/hotspot/src/share/vm/classfile/protectionDomainCache.hpp
+++ b/hotspot/src/share/vm/classfile/protectionDomainCache.hpp
@@ -51,7 +51,6 @@
     f->do_oop(literal_addr());
   }
 
-  void print() PRODUCT_RETURN;
   void verify();
 };
 
@@ -67,7 +66,7 @@
 class ProtectionDomainCacheTable : public Hashtable<oop, mtClass> {
   friend class VMStructs;
 private:
-  ProtectionDomainCacheEntry* bucket(int i) {
+  ProtectionDomainCacheEntry* bucket(int i) const {
     return (ProtectionDomainCacheEntry*) Hashtable<oop, mtClass>::bucket(i);
   }
 
@@ -96,7 +95,7 @@
   // GC support
   void oops_do(OopClosure* f);
 
-  void print() PRODUCT_RETURN;
+  void print_on(outputStream* st) const;
   void verify();
 };
 
diff --git a/hotspot/src/share/vm/classfile/stringTable.cpp b/hotspot/src/share/vm/classfile/stringTable.cpp
index 57090a8..7efbfd9 100644
--- a/hotspot/src/share/vm/classfile/stringTable.cpp
+++ b/hotspot/src/share/vm/classfile/stringTable.cpp
@@ -37,6 +37,7 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "services/diagnosticCommand.hpp"
 #include "utilities/hashtable.inline.hpp"
 #include "utilities/macros.hpp"
 #if INCLUDE_ALL_GCS
@@ -439,7 +440,7 @@
 
 void StringTable::dump(outputStream* st, bool verbose) {
   if (!verbose) {
-    the_table()->dump_table(st, "StringTable");
+    the_table()->print_table_statistics(st, "StringTable");
   } else {
     Thread* THREAD = Thread::current();
     st->print_cr("VERSION: 1.1");
diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp
index 548be92..7c8cda6 100644
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp
@@ -36,6 +36,7 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "services/diagnosticCommand.hpp"
 #include "utilities/hashtable.inline.hpp"
 
 // --------------------------------------------------------------------------
@@ -550,7 +551,7 @@
 
 void SymbolTable::dump(outputStream* st, bool verbose) {
   if (!verbose) {
-    the_table()->dump_table(st, "SymbolTable");
+    the_table()->print_table_statistics(st, "SymbolTable");
   } else {
     st->print_cr("VERSION: 1.0");
     for (int i = 0; i < the_table()->table_size(); ++i) {
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp
index c34e9e1..215afaa 100644
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp
@@ -74,6 +74,7 @@
 #include "runtime/orderAccess.inline.hpp"
 #include "runtime/signature.hpp"
 #include "services/classLoadingService.hpp"
+#include "services/diagnosticCommand.hpp"
 #include "services/threadService.hpp"
 #include "trace/traceMacros.hpp"
 #include "utilities/macros.hpp"
@@ -2808,33 +2809,32 @@
 }
 
 // ----------------------------------------------------------------------------
-void SystemDictionary::print_shared(bool details) {
-  shared_dictionary()->print(details);
+void SystemDictionary::print_shared(outputStream *st) {
+  shared_dictionary()->print_on(st);
 }
 
-void SystemDictionary::print(bool details) {
+void SystemDictionary::print_on(outputStream *st) {
   if (shared_dictionary() != NULL) {
     tty->print_cr("Shared Dictionary");
-    shared_dictionary()->print(details);
+    shared_dictionary()->print_on(st);
   }
 
   GCMutexLocker mu(SystemDictionary_lock);
 
-  ClassLoaderDataGraph::print_dictionary(details);
+  ClassLoaderDataGraph::print_dictionary(st);
 
   // Placeholders
-  placeholders()->print();
-  tty->cr();
+  placeholders()->print_on(st);
+  st->cr();
 
   // loader constraints - print under SD_lock
-  constraints()->print();
-  tty->cr();
+  constraints()->print_on(st);
+  st->cr();
 
-  _pd_cache_table->print();
-  tty->cr();
+  _pd_cache_table->print_on(st);
+  st->cr();
 }
 
-
 void SystemDictionary::verify() {
   guarantee(constraints() != NULL,
             "Verify of loader constraints failed");
@@ -2855,6 +2855,44 @@
   _pd_cache_table->verify();
 }
 
+void SystemDictionary::dump(outputStream *st, bool verbose) {
+  assert_locked_or_safepoint(SystemDictionary_lock);
+  if (verbose) {
+    print_on(st);
+  } else {
+    ClassLoaderDataGraph::print_dictionary_statistics(st);
+    placeholders()->print_table_statistics(st, "Placeholder Table");
+    constraints()->print_table_statistics(st, "LoaderConstraints Table");
+    _pd_cache_table->print_table_statistics(st, "ProtectionDomainCache Table");
+  }
+}
+
+// Utility for dumping dictionaries.
+SystemDictionaryDCmd::SystemDictionaryDCmd(outputStream* output, bool heap) :
+                                 DCmdWithParser(output, heap),
+  _verbose("-verbose", "Dump the content of each dictionary entry for all class loaders",
+           "BOOLEAN", false, "false") {
+  _dcmdparser.add_dcmd_option(&_verbose);
+}
+
+void SystemDictionaryDCmd::execute(DCmdSource source, TRAPS) {
+  VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpSysDict,
+                         _verbose.value());
+  VMThread::execute(&dumper);
+}
+
+int SystemDictionaryDCmd::num_arguments() {
+  ResourceMark rm;
+  SystemDictionaryDCmd* dcmd = new SystemDictionaryDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+
 // caller needs ResourceMark
 const char* SystemDictionary::loader_name(const oop loader) {
   return ((loader) == NULL ? "<bootloader>" :
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp
index 94df816..985bbc3 100644
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp
@@ -388,8 +388,10 @@
   static void set_shared_dictionary(HashtableBucket<mtClass>* t, int length,
                                     int number_of_entries);
   // Printing
-  static void print(bool details = true);
-  static void print_shared(bool details = true);
+  static void print() { return print_on(tty); }
+  static void print_on(outputStream* st);
+  static void print_shared(outputStream* st);
+  static void dump(outputStream* st, bool verbose);
 
   // Monotonically increasing counter which grows as classes are
   // loaded or modifications such as hot-swapping or setting/removing
diff --git a/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp b/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp
index bad5850..ed22e48 100644
--- a/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "aot/aotLoader.hpp"
 #include "classfile/symbolTable.hpp"
+#include "classfile/stringTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "code/codeCache.hpp"
diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp
index 6760fa3..b7e0afa 100644
--- a/hotspot/src/share/vm/memory/filemap.cpp
+++ b/hotspot/src/share/vm/memory/filemap.cpp
@@ -26,6 +26,7 @@
 #include "classfile/classLoader.hpp"
 #include "classfile/compactHashtable.inline.hpp"
 #include "classfile/sharedClassUtil.hpp"
+#include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "classfile/altHashing.hpp"
diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp
index 496b5a5..f0f2412 100644
--- a/hotspot/src/share/vm/memory/filemap.hpp
+++ b/hotspot/src/share/vm/memory/filemap.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -25,8 +25,10 @@
 #ifndef SHARE_VM_MEMORY_FILEMAP_HPP
 #define SHARE_VM_MEMORY_FILEMAP_HPP
 
+#include "classfile/classLoader.hpp"
 #include "memory/metaspaceShared.hpp"
 #include "memory/metaspace.hpp"
+#include "memory/universe.hpp"
 #include "utilities/align.hpp"
 
 // Layout of the file:
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp
index 9c313b0..f47eac7 100644
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp
@@ -30,6 +30,7 @@
 #include "classfile/placeholders.hpp"
 #include "classfile/sharedClassUtil.hpp"
 #include "classfile/symbolTable.hpp"
+#include "classfile/stringTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
 #include "code/codeCache.hpp"
@@ -1328,7 +1329,7 @@
   if (PrintSharedArchiveAndExit) {
     if (PrintSharedDictionary) {
       tty->print_cr("\nShared classes:\n");
-      SystemDictionary::print_shared(false);
+      SystemDictionary::print_shared(tty);
     }
     if (_archive_loading_failed) {
       tty->print_cr("archive is invalid");
diff --git a/hotspot/src/share/vm/runtime/commandLineFlagConstraintsCompiler.cpp b/hotspot/src/share/vm/runtime/commandLineFlagConstraintsCompiler.cpp
index 84b5dbf..f74b502 100644
--- a/hotspot/src/share/vm/runtime/commandLineFlagConstraintsCompiler.cpp
+++ b/hotspot/src/share/vm/runtime/commandLineFlagConstraintsCompiler.cpp
@@ -23,14 +23,16 @@
  */
 
 #include "precompiled.hpp"
+#include "code/relocInfo.hpp"
+#include "compiler/compilerDefinitions.hpp"
 #include "oops/metadata.hpp"
 #include "runtime/os.hpp"
-#include "code/relocInfo.hpp"
 #include "interpreter/invocationCounter.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/commandLineFlagConstraintsCompiler.hpp"
 #include "runtime/commandLineFlagRangeList.hpp"
 #include "runtime/globals.hpp"
+#include "runtime/globals_extension.hpp"
 #include "utilities/defaultStream.hpp"
 
 Flag::Error AliasLevelConstraintFunc(intx value, bool verbose) {
diff --git a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp
index 1a2abe1..d96eb14 100644
--- a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp
+++ b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp
@@ -27,9 +27,11 @@
 #include "classfile/symbolTable.hpp"
 #include "gc/shared/referenceProcessor.hpp"
 #include "prims/jvm.h"
+#include "oops/markOop.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/commandLineFlagConstraintList.hpp"
 #include "runtime/commandLineFlagRangeList.hpp"
+#include "runtime/globals_extension.hpp"
 #include "runtime/os.hpp"
 #include "runtime/task.hpp"
 #include "utilities/defaultStream.hpp"
diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp
index 422c4bd..ffe97f2 100644
--- a/hotspot/src/share/vm/runtime/thread.cpp
+++ b/hotspot/src/share/vm/runtime/thread.cpp
@@ -848,6 +848,13 @@
   }
 }
 
+void Thread::print_value_on(outputStream* st) const {
+  if (is_Named_thread()) {
+    st->print(" \"%s\" ", name());
+  }
+  st->print(INTPTR_FORMAT, p2i(this));   // print address
+}
+
 #ifdef ASSERT
 void Thread::print_owned_locks_on(outputStream* st) const {
   Monitor *cur = _owned_locks;
diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp
index 6027967..60ed616 100644
--- a/hotspot/src/share/vm/runtime/thread.hpp
+++ b/hotspot/src/share/vm/runtime/thread.hpp
@@ -574,6 +574,7 @@
   virtual void print_on(outputStream* st) const;
   void print() const { print_on(tty); }
   virtual void print_on_error(outputStream* st, char* buf, int buflen) const;
+  void print_value_on(outputStream* st) const;
 
   // Debug-only code
 #ifdef ASSERT
diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp
index 5c71a59b1..984700e 100644
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp
@@ -32,6 +32,7 @@
 #include "classfile/dictionary.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "code/codeBlob.hpp"
 #include "code/codeCache.hpp"
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp
index 3e802db..8a7339b 100644
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp
@@ -84,6 +84,7 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemDictionaryDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHierarchyDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp
index 0e7b48e..593e5bb 100644
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -25,6 +25,9 @@
 #ifndef SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
 #define SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
 
+#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/os.hpp"
@@ -724,4 +727,116 @@
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+///////////////////////////////////////////////////////////////////////
+//
+// jcmd command support for symbol table, string table and system dictionary dumping:
+//   VM.symboltable -verbose: for dumping the symbol table
+//   VM.stringtable -verbose: for dumping the string table
+//   VM.systemdictionary -verbose: for dumping the system dictionary table
+//
+class VM_DumpHashtable : public VM_Operation {
+private:
+  outputStream* _out;
+  int _which;
+  bool _verbose;
+public:
+  enum {
+    DumpSymbols = 1 << 0,
+    DumpStrings = 1 << 1,
+    DumpSysDict = 1 << 2  // not implemented yet
+  };
+  VM_DumpHashtable(outputStream* out, int which, bool verbose) {
+    _out = out;
+    _which = which;
+    _verbose = verbose;
+  }
+
+  virtual VMOp_Type type() const { return VMOp_DumpHashtable; }
+
+  virtual void doit() {
+    switch (_which) {
+    case DumpSymbols:
+      SymbolTable::dump(_out, _verbose);
+      break;
+    case DumpStrings:
+      StringTable::dump(_out, _verbose);
+      break;
+    case DumpSysDict:
+      SystemDictionary::dump(_out, _verbose);
+      break;
+    default:
+      ShouldNotReachHere();
+    }
+  }
+};
+
+class SymboltableDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<bool> _verbose;
+public:
+  SymboltableDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "VM.symboltable";
+  }
+  static const char* description() {
+    return "Dump symbol table.";
+  }
+  static const char* impact() {
+    return "Medium: Depends on Java content.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
+class StringtableDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<bool> _verbose;
+public:
+  StringtableDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "VM.stringtable";
+  }
+  static const char* description() {
+    return "Dump string table.";
+  }
+  static const char* impact() {
+    return "Medium: Depends on Java content.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
+class SystemDictionaryDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<bool> _verbose;
+public:
+  SystemDictionaryDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "VM.systemdictionary";
+  }
+  static const char* description() {
+    return "Prints the statistics for dictionary hashtable sizes and bucket length";
+  }
+  static const char* impact() {
+      return "Medium: Depends on Java content.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
 #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp
index 018981f..58a441e 100644
--- a/hotspot/src/share/vm/utilities/hashtable.cpp
+++ b/hotspot/src/share/vm/utilities/hashtable.cpp
@@ -231,24 +231,42 @@
   }
 }
 
-template <class T, MEMFLAGS F> int RehashableHashtable<T, F>::literal_size(Symbol *symbol) {
+// For oops and Strings the size of the literal is interesting. For other types, nobody cares.
+static int literal_size(ConstantPool*) { return 0; }
+static int literal_size(Klass*)        { return 0; }
+#if INCLUDE_ALL_GCS
+static int literal_size(nmethod*)      { return 0; }
+#endif
+
+static int literal_size(Symbol *symbol) {
   return symbol->size() * HeapWordSize;
 }
 
-template <class T, MEMFLAGS F> int RehashableHashtable<T, F>::literal_size(oop oop) {
+static int literal_size(oop obj) {
   // NOTE: this would over-count if (pre-JDK8) java_lang_Class::has_offset_field() is true,
   // and the String.value array is shared by several Strings. However, starting from JDK8,
   // the String.value array is not shared anymore.
-  assert(oop != NULL && oop->klass() == SystemDictionary::String_klass(), "only strings are supported");
-  return (oop->size() + java_lang_String::value(oop)->size()) * HeapWordSize;
+  if (obj == NULL) {
+    return 0;
+  } else if (obj->klass() == SystemDictionary::String_klass()) {
+    return (obj->size() + java_lang_String::value(obj)->size()) * HeapWordSize;
+  } else {
+    return obj->size();
+  }
 }
 
+
 // Dump footprint and bucket length statistics
 //
 // Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
-// add a new function Hashtable<T, F>::literal_size(MyNewType lit)
+// add a new function static int literal_size(MyNewType lit)
+// because I can't get template <class T> int literal_size(T) to pick the specializations for Symbol and oop.
+//
+// The StringTable and SymbolTable dumping print how much footprint is used by the String and Symbol
+// literals.
 
-template <class T, MEMFLAGS F> void RehashableHashtable<T, F>::dump_table(outputStream* st, const char *table_name) {
+template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outputStream* st,
+                                                                            const char *table_name) {
   NumberSeq summary;
   int literal_bytes = 0;
   for (int i = 0; i < this->table_size(); ++i) {
@@ -267,14 +285,16 @@
   int entry_bytes  = (int)num_entries * sizeof(HashtableEntry<T, F>);
   int total_bytes = literal_bytes +  bucket_bytes + entry_bytes;
 
-  double bucket_avg  = (num_buckets <= 0) ? 0 : (bucket_bytes  / num_buckets);
-  double entry_avg   = (num_entries <= 0) ? 0 : (entry_bytes   / num_entries);
-  double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries);
+  int bucket_size  = (num_buckets <= 0) ? 0 : (bucket_bytes  / num_buckets);
+  int entry_size   = (num_entries <= 0) ? 0 : (entry_bytes   / num_entries);
 
   st->print_cr("%s statistics:", table_name);
-  st->print_cr("Number of buckets       : %9d = %9d bytes, avg %7.3f", (int)num_buckets, bucket_bytes,  bucket_avg);
-  st->print_cr("Number of entries       : %9d = %9d bytes, avg %7.3f", (int)num_entries, entry_bytes,   entry_avg);
-  st->print_cr("Number of literals      : %9d = %9d bytes, avg %7.3f", (int)num_entries, literal_bytes, literal_avg);
+  st->print_cr("Number of buckets       : %9d = %9d bytes, each %d", (int)num_buckets, bucket_bytes,  bucket_size);
+  st->print_cr("Number of entries       : %9d = %9d bytes, each %d", (int)num_entries, entry_bytes,   entry_size);
+  if (literal_bytes != 0) {
+    double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries);
+    st->print_cr("Number of literals      : %9d = %9d bytes, avg %7.3f", (int)num_entries, literal_bytes, literal_avg);
+  }
   st->print_cr("Total footprint         : %9s = %9d bytes", "", total_bytes);
   st->print_cr("Average bucket size     : %9.3f", summary.avg());
   st->print_cr("Variance of bucket size : %9.3f", summary.variance());
@@ -300,7 +320,6 @@
   *top += len;
 }
 
-
 #ifndef PRODUCT
 
 template <class T, MEMFLAGS F> void Hashtable<T, F>::print() {
@@ -398,4 +417,3 @@
 template void BasicHashtable<mtModule>::verify_table<PackageEntry>(char const*);
 template void BasicHashtable<mtClass>::verify_table<ProtectionDomainCacheEntry>(char const*);
 template void BasicHashtable<mtClass>::verify_table<PlaceholderEntry>(char const*);
-
diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp
index 733a623..bb32ea0 100644
--- a/hotspot/src/share/vm/utilities/hashtable.hpp
+++ b/hotspot/src/share/vm/utilities/hashtable.hpp
@@ -226,14 +226,14 @@
   // is mt-safe wrt. to other calls of this method.
   void bulk_free_entries(BucketUnlinkContext* context);
 public:
-  int table_size() { return _table_size; }
+  int table_size() const { return _table_size; }
   void set_entry(int index, BasicHashtableEntry<F>* entry);
 
   void add_entry(int index, BasicHashtableEntry<F>* entry);
 
   void free_entry(BasicHashtableEntry<F>* entry);
 
-  int number_of_entries() { return _number_of_entries; }
+  int number_of_entries() const { return _number_of_entries; }
 
   template <class T> void verify_table(const char* table_name) PRODUCT_RETURN;
 };
@@ -261,7 +261,9 @@
     return this->hash_to_index(compute_hash(name));
   }
 
-protected:
+  void print_table_statistics(outputStream* st, const char *table_name);
+
+ protected:
 
   // Table entry management
   HashtableEntry<T, F>* new_entry(unsigned int hashValue, T obj);
@@ -306,18 +308,6 @@
   static bool use_alternate_hashcode();
   static juint seed();
 
-  static int literal_size(Symbol *symbol);
-  static int literal_size(oop oop);
-
-  // The following two are currently not used, but are needed anyway because some
-  // C++ compilers (MacOS and Solaris) force the instantiation of
-  // Hashtable<ConstantPool*, mtClass>::dump_table() even though we never call this function
-  // in the VM code.
-  static int literal_size(ConstantPool *cp) {Unimplemented(); return 0;}
-  static int literal_size(Klass *k)         {Unimplemented(); return 0;}
-
-  void dump_table(outputStream* st, const char *table_name);
-
  private:
   static juint _seed;
 };
diff --git a/hotspot/test/runtime/SharedArchiveFile/DumpSymbolAndStringTable.java b/hotspot/test/runtime/SharedArchiveFile/DumpSymbolAndStringTable.java
index 97e8e58..821cb74 100644
--- a/hotspot/test/runtime/SharedArchiveFile/DumpSymbolAndStringTable.java
+++ b/hotspot/test/runtime/SharedArchiveFile/DumpSymbolAndStringTable.java
@@ -56,5 +56,25 @@
         } catch (RuntimeException e) {
             output.shouldContain("Unknown diagnostic command");
         }
+
+        pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.systemdictionary"});
+        output = CDSTestUtils.executeAndLog(pb, "jcmd-systemdictionary");
+        try {
+            output.shouldContain("System Dictionary for jdk/internal/loader/ClassLoaders$AppClassLoader statistics:");
+            output.shouldContain("Number of buckets");
+            output.shouldContain("Number of entries");
+            output.shouldContain("Maximum bucket size");
+        } catch (RuntimeException e) {
+            output.shouldContain("Unknown diagnostic command");
+        }
+
+        pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.systemdictionary", "-verbose"});
+        output = CDSTestUtils.executeAndLog(pb, "jcmd-systemdictionary");
+        try {
+            output.shouldContain("Dictionary for class loader 0x");
+            output.shouldContain("^java.lang.String");
+        } catch (RuntimeException e) {
+            output.shouldContain("Unknown diagnostic command");
+        }
     }
 }