8201572: Improve Metaspace Statistics
authorstuefe
Tue, 24 Apr 2018 18:06:32 +0200
changeset 49980 57dd7b4ba338
parent 49979 b70281f5146e
child 49981 bd0a95bec96b
8201572: Improve Metaspace Statistics Reviewed-by: adinn, zgu
src/hotspot/share/memory/metachunk.hpp
src/hotspot/share/memory/metaspace.cpp
src/hotspot/share/memory/metaspace.hpp
src/hotspot/share/memory/metaspace/metaspaceCommon.cpp
src/hotspot/share/memory/metaspace/metaspaceCommon.hpp
src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp
src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp
src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp
src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp
src/hotspot/share/runtime/vm_operations.cpp
src/hotspot/share/runtime/vm_operations.hpp
src/hotspot/share/services/diagnosticCommand.cpp
src/hotspot/share/services/diagnosticCommand.hpp
src/hotspot/share/services/memReporter.cpp
src/hotspot/share/services/memTracker.cpp
src/hotspot/share/services/metaspaceDCmd.cpp
src/hotspot/share/services/nmtCommon.cpp
src/hotspot/share/services/virtualMemoryTracker.cpp
src/hotspot/share/utilities/ostream.cpp
src/hotspot/share/utilities/ostream.hpp
src/hotspot/share/utilities/vmError.cpp
test/hotspot/jtreg/TEST.ROOT
test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java
test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java
--- a/src/hotspot/share/memory/metachunk.hpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/memory/metachunk.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -110,6 +110,9 @@
 size_t get_size_for_nonhumongous_chunktype(ChunkIndex chunk_type, bool is_class);
 ChunkIndex get_chunk_type_by_size(size_t size, bool is_class);
 
+ChunkIndex next_chunk_index(ChunkIndex i);
+ChunkIndex prev_chunk_index(ChunkIndex i);
+
 // Returns a descriptive name for a chunk type.
 const char* chunk_size_name(ChunkIndex index);
 
@@ -184,7 +187,7 @@
   // Alignment of each allocation in the chunks.
   static size_t object_alignment();
 
-  // Size of the Metachunk header, including alignment.
+  // Size of the Metachunk header, in words, including alignment.
   static size_t overhead();
 
   Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container);
--- a/src/hotspot/share/memory/metaspace.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/memory/metaspace.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -33,6 +33,8 @@
 #include "memory/freeList.inline.hpp"
 #include "memory/metachunk.hpp"
 #include "memory/metaspace.hpp"
+#include "memory/metaspace/metaspaceCommon.hpp"
+#include "memory/metaspace/metaspaceStatistics.hpp"
 #include "memory/metaspaceGCThresholdUpdater.hpp"
 #include "memory/metaspaceShared.hpp"
 #include "memory/metaspaceTracer.hpp"
@@ -43,14 +45,18 @@
 #include "runtime/init.hpp"
 #include "runtime/java.hpp"
 #include "runtime/mutex.hpp"
+#include "runtime/mutexLocker.hpp"
 #include "runtime/orderAccess.inline.hpp"
 #include "services/memTracker.hpp"
 #include "services/memoryService.hpp"
 #include "utilities/align.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
 #include "utilities/macros.hpp"
 
+using namespace metaspace::internals;
+
 typedef BinaryTreeDictionary<Metablock, FreeList<Metablock> > BlockTreeDictionary;
 typedef BinaryTreeDictionary<Metachunk, FreeList<Metachunk> > ChunkTreeDictionary;
 
@@ -70,6 +76,30 @@
 
 DEBUG_ONLY(bool Metaspace::_frozen = false;)
 
+// Internal statistics.
+#ifdef ASSERT
+static struct {
+  // Number of allocations.
+  uintx num_allocs;
+  // Number of times a ClassLoaderMetaspace was born...
+  uintx num_metaspace_births;
+  // ... and died.
+  uintx num_metaspace_deaths;
+  // Number of times VirtualSpaceListNodes were created...
+  uintx num_vsnodes_created;
+  // ... and purged.
+  uintx num_vsnodes_purged;
+  // Number of times we expanded the committed section of the space.
+  uintx num_committed_space_expanded;
+  // Number of deallocations
+  uintx num_deallocs;
+  // Number of deallocations triggered from outside ("real" deallocations).
+  uintx num_external_deallocs;
+  // Number of times an allocation was satisfied from deallocated blocks.
+  uintx num_allocs_from_deallocated_blocks;
+} g_internal_statistics;
+#endif
+
 enum ChunkSizes {    // in words.
   ClassSpecializedChunk = 128,
   SpecializedChunk = 128,
@@ -133,33 +163,33 @@
   return (ChunkIndex)-1;
 }
 
-
-static ChunkIndex next_chunk_index(ChunkIndex i) {
+ChunkIndex next_chunk_index(ChunkIndex i) {
   assert(i < NumberOfInUseLists, "Out of bound");
   return (ChunkIndex) (i+1);
 }
 
-static ChunkIndex prev_chunk_index(ChunkIndex i) {
+ChunkIndex prev_chunk_index(ChunkIndex i) {
   assert(i > ZeroIndex, "Out of bound");
   return (ChunkIndex) (i-1);
 }
 
-static const char* scale_unit(size_t scale) {
-  switch(scale) {
-    case 1: return "BYTES";
-    case K: return "KB";
-    case M: return "MB";
-    case G: return "GB";
-    default:
-      ShouldNotReachHere();
-      return NULL;
-  }
+static const char* space_type_name(Metaspace::MetaspaceType t) {
+  const char* s = NULL;
+  switch (t) {
+    case Metaspace::StandardMetaspaceType: s = "Standard"; break;
+    case Metaspace::BootMetaspaceType: s = "Boot"; break;
+    case Metaspace::AnonymousMetaspaceType: s = "Anonymous"; break;
+    case Metaspace::ReflectionMetaspaceType: s = "Reflection"; break;
+    default: ShouldNotReachHere();
+  }
+  return s;
 }
 
 volatile intptr_t MetaspaceGC::_capacity_until_GC = 0;
 uint MetaspaceGC::_shrink_factor = 0;
 bool MetaspaceGC::_should_concurrent_collect = false;
 
+
 typedef class FreeList<Metachunk> ChunkList;
 
 // Manages the global free lists of chunks.
@@ -240,19 +270,6 @@
 
  public:
 
-  struct ChunkManagerStatistics {
-    size_t num_by_type[NumberOfFreeLists];
-    size_t single_size_by_type[NumberOfFreeLists];
-    size_t total_size_by_type[NumberOfFreeLists];
-    size_t num_humongous_chunks;
-    size_t total_size_humongous_chunks;
-  };
-
-  void locked_get_statistics(ChunkManagerStatistics* stat) const;
-  void get_statistics(ChunkManagerStatistics* stat) const;
-  static void print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale);
-
-
   ChunkManager(bool is_class)
       : _is_class(is_class), _free_chunks_total(0), _free_chunks_count(0) {
     _free_chunks[SpecializedIndex].set_size(get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class));
@@ -358,9 +375,9 @@
 
   void print_on(outputStream* st) const;
 
-  // Prints composition for both non-class and (if available)
-  // class chunk manager.
-  static void print_all_chunkmanagers(outputStream* out, size_t scale = 1);
+  // Fill in current statistic values to the given statistics object.
+  void collect_statistics(ChunkManagerStatistics* out) const;
+
 };
 
 class SmallBlocks : public CHeapObj<mtClass> {
@@ -384,6 +401,7 @@
     }
   }
 
+  // Returns the total size, in words, of all blocks, across all block sizes.
   size_t total_size() const {
     size_t result = 0;
     for (uint i = _small_block_min_size; i < _small_block_max_size; i++) {
@@ -393,6 +411,16 @@
     return result;
   }
 
+  // Returns the total number of all blocks across all block sizes.
+  uintx total_num_blocks() const {
+    uintx result = 0;
+    for (uint i = _small_block_min_size; i < _small_block_max_size; i++) {
+      uint k = i - _small_block_min_size;
+      result = result + _small_lists[k].count();
+    }
+    return result;
+  }
+
   static uint small_block_max_size() { return _small_block_max_size; }
   static uint small_block_min_size() { return _small_block_min_size; }
 
@@ -445,6 +473,7 @@
   MetaWord* get_block(size_t word_size);
   void return_block(MetaWord* p, size_t word_size);
 
+  // Returns the total size, in words, of all blocks kept in this structure.
   size_t total_size() const  {
     size_t result = dictionary()->total_size();
     if (_small_blocks != NULL) {
@@ -453,6 +482,15 @@
     return result;
   }
 
+  // Returns the number of all blocks kept in this structure.
+  uintx num_blocks() const {
+    uintx result = dictionary()->total_free_blocks();
+    if (_small_blocks != NULL) {
+      result = result + _small_blocks->total_num_blocks();
+    }
+    return result;
+  }
+
   static size_t min_dictionary_size()   { return TreeChunk<Metablock, FreeList<Metablock> >::min_size(); }
   void print_on(outputStream* st) const;
 };
@@ -858,7 +896,8 @@
   void retire(ChunkManager* chunk_manager);
 
 
-  void print_on(outputStream* st) const;
+  void print_on(outputStream* st) const                 { print_on(st, K); }
+  void print_on(outputStream* st, size_t scale) const;
   void print_map(outputStream* st, bool is_class) const;
 
   // Debug support
@@ -876,6 +915,12 @@
          SIZE_FORMAT_HEX " is not aligned to "               \
          SIZE_FORMAT, (size_t)(uintptr_t)value, (alignment))
 
+#define assert_counter(expected_value, real_value, msg) \
+  assert( (expected_value) == (real_value),             \
+         "Counter mismatch (%s): expected " SIZE_FORMAT \
+         ", but got: " SIZE_FORMAT ".", msg, expected_value, \
+         real_value);
+
 // Decide if large pages should be committed when the memory is reserved.
 static bool should_commit_large_pages_when_reserving(size_t bytes) {
   if (UseLargePages && UseLargePagesInMetaspace && !os::can_commit_large_page_memory()) {
@@ -1182,7 +1227,8 @@
   // Unlink empty VirtualSpaceNodes and free it.
   void purge(ChunkManager* chunk_manager);
 
-  void print_on(outputStream* st) const;
+  void print_on(outputStream* st) const                 { print_on(st, K); }
+  void print_on(outputStream* st, size_t scale) const;
   void print_map(outputStream* st) const;
 
   class VirtualSpaceListIterator : public StackObj {
@@ -1219,6 +1265,7 @@
 
 int Metadebug::_allocation_fail_alot_count = 0;
 
+
 //  SpaceManager - used by Metaspace to handle allocations
 class SpaceManager : public CHeapObj<mtClass> {
   friend class ClassLoaderMetaspace;
@@ -1248,12 +1295,13 @@
   // metadata space to a SpaceManager
   static uint const _anon_and_delegating_metadata_specialize_chunk_limit;
 
-  // Sum of all space in allocated chunks
-  size_t _allocated_blocks_words;
-
-  // Sum of all allocated chunks
-  size_t _allocated_chunks_words;
-  size_t _allocated_chunks_count;
+  // Some running counters, but lets keep their number small to not add to much to
+  // the per-classloader footprint.
+  // Note: capacity = used + free + waste + overhead. We do not keep running counters for
+  // free and waste. Their sum can be deduced from the three other values.
+  size_t _overhead_words;
+  size_t _capacity_words;
+  size_t _used_words;
 
   // Free lists of blocks are per SpaceManager since they
   // are assumed to be in chunks in use by the SpaceManager
@@ -1288,6 +1336,12 @@
 
   Mutex* lock() const { return _lock; }
 
+  // Adds to the given statistic object. Expects to be locked with lock().
+  void add_to_statistics_locked(SpaceManagerStatistics* out) const;
+
+  // Verify internal counters against the current state. Expects to be locked with lock().
+  DEBUG_ONLY(void verify_metrics_locked() const;)
+
  protected:
   void initialize();
 
@@ -1318,25 +1372,21 @@
 
   size_t medium_chunk_bunch()     const { return medium_chunk_size() * MediumChunkMultiple; }
 
-  size_t allocated_blocks_words() const { return _allocated_blocks_words; }
-  size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; }
-  size_t allocated_chunks_words() const { return _allocated_chunks_words; }
-  size_t allocated_chunks_bytes() const { return _allocated_chunks_words * BytesPerWord; }
-  size_t allocated_chunks_count() const { return _allocated_chunks_count; }
-
   bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); }
 
-  // Increment the per Metaspace and global running sums for Metachunks
-  // by the given size.  This is used when a Metachunk to added to
-  // the in-use list.
-  void inc_size_metrics(size_t words);
-  // Increment the per Metaspace and global running sums Metablocks by the given
-  // size.  This is used when a Metablock is allocated.
-  void inc_used_metrics(size_t words);
-  // Delete the portion of the running sums for this SpaceManager. That is,
-  // the globals running sums for the Metachunks and Metablocks are
-  // decremented for all the Metachunks in-use by this SpaceManager.
-  void dec_total_from_size_metrics();
+  size_t capacity_words() const     { return _capacity_words; }
+  size_t used_words() const         { return _used_words; }
+  size_t overhead_words() const     { return _overhead_words; }
+
+  // Adjust local, global counters after a new chunk has been added.
+  void account_for_new_chunk(const Metachunk* new_chunk);
+
+  // Adjust local, global counters after space has been allocated from the current chunk.
+  void account_for_allocation(size_t words);
+
+  // Adjust global counters just before the SpaceManager dies, after all its chunks
+  // have been returned to the freelist.
+  void account_for_spacemanager_death();
 
   // Adjust the initial chunk size to match one of the fixed chunk list sizes,
   // or return the unadjusted size if the requested size is humongous.
@@ -1346,13 +1396,7 @@
   // Get the initial chunks size for this metaspace type.
   size_t get_initial_chunk_size(Metaspace::MetaspaceType type) const;
 
-  size_t sum_capacity_in_chunks_in_use() const;
-  size_t sum_used_in_chunks_in_use() const;
-  size_t sum_free_in_chunks_in_use() const;
-  size_t sum_waste_in_chunks_in_use() const;
-  size_t sum_waste_in_chunks_in_use(ChunkIndex index ) const;
-
-  size_t sum_count_in_chunks_in_use();
+  // Todo: remove this once we have counters by chunk type.
   size_t sum_count_in_chunks_in_use(ChunkIndex i);
 
   Metachunk* get_new_chunk(size_t chunk_word_size);
@@ -1381,15 +1425,11 @@
 
   // debugging support.
 
-  void dump(outputStream* const out) const;
   void print_on(outputStream* st) const;
   void locked_print_chunks_in_use_on(outputStream* st) const;
 
   void verify();
   void verify_chunk_size(Metachunk* chunk);
-#ifdef ASSERT
-  void verify_allocated_blocks_words();
-#endif
 
   // This adjusts the size given to be greater than the minimum allocation size in
   // words for data in metaspace.  Esentially the minimum size is currently 3 words.
@@ -1404,6 +1444,13 @@
 
     return raw_word_size;
   }
+
+  // Adds to the given statistic object.
+  void add_to_statistics(SpaceManagerStatistics* out) const;
+
+  // Verify internal counters against the current state.
+  DEBUG_ONLY(void verify_metrics() const;)
+
 };
 
 uint const SpaceManager::_small_chunk_limit = 4;
@@ -1658,6 +1705,7 @@
       ls.print("VirtualSpaceNode::take_from_committed() not available " SIZE_FORMAT " words ", chunk_word_size);
       // Dump some information about the virtual space that is nearly full
       print_on(&ls);
+      ls.cr(); // ~LogStream does not autoflush.
     }
     return NULL;
   }
@@ -1704,6 +1752,7 @@
   if (result) {
     log_trace(gc, metaspace, freelist)("Expanded %s virtual space list node by " SIZE_FORMAT " words.",
               (is_class() ? "class" : "non-class"), commit);
+    DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_committed_space_expanded));
   } else {
     log_trace(gc, metaspace, freelist)("Failed to expand %s virtual space list node by " SIZE_FORMAT " words.",
               (is_class() ? "class" : "non-class"), commit);
@@ -1763,15 +1812,22 @@
   return result;
 }
 
-void VirtualSpaceNode::print_on(outputStream* st) const {
-  size_t used = used_words_in_vs();
-  size_t capacity = capacity_words_in_vs();
+void VirtualSpaceNode::print_on(outputStream* st, size_t scale) const {
+  size_t used_words = used_words_in_vs();
+  size_t commit_words = committed_words();
+  size_t res_words = reserved_words();
   VirtualSpace* vs = virtual_space();
-  st->print_cr("   space @ " PTR_FORMAT " " SIZE_FORMAT "K, " SIZE_FORMAT_W(3) "%% used "
-           "[" PTR_FORMAT ", " PTR_FORMAT ", "
+
+  st->print("node @" PTR_FORMAT ": ", p2i(this));
+  st->print("reserved=");
+  print_scaled_words(st, res_words, scale);
+  st->print(", committed=");
+  print_scaled_words_and_percentage(st, commit_words, res_words, scale);
+  st->print(", used=");
+  print_scaled_words_and_percentage(st, used_words, res_words, scale);
+  st->cr();
+  st->print("   [" PTR_FORMAT ", " PTR_FORMAT ", "
            PTR_FORMAT ", " PTR_FORMAT ")",
-           p2i(vs), capacity / K,
-           capacity == 0 ? 0 : used * 100 / capacity,
            p2i(bottom()), p2i(top()), p2i(end()),
            p2i(vs->high_boundary()));
 }
@@ -1993,6 +2049,7 @@
     if (vsl->container_count() == 0 && vsl != current_virtual_space()) {
       log_trace(gc, metaspace, freelist)("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT
                                          ", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs());
+      DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_purged));
       // Unlink it from the list
       if (prev_vsl == vsl) {
         // This is the case of the current node being the first node.
@@ -2140,6 +2197,7 @@
     // ensure lock-free iteration sees fully initialized node
     OrderAccess::storestore();
     link_vs(new_entry);
+    DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created));
     return true;
   }
 }
@@ -2163,6 +2221,7 @@
     VirtualSpaceNode* vsl = current_virtual_space();
     ResourceMark rm;
     vsl->print_on(&ls);
+    ls.cr(); // ~LogStream does not autoflush.
   }
 }
 
@@ -2288,11 +2347,14 @@
    return next;
 }
 
-void VirtualSpaceList::print_on(outputStream* st) const {
+void VirtualSpaceList::print_on(outputStream* st, size_t scale) const {
+  st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT,
+      _virtual_space_count, p2i(_current_virtual_space));
   VirtualSpaceListIterator iter(virtual_space_list());
   while (iter.repeat()) {
+    st->cr();
     VirtualSpaceNode* node = iter.get_next();
-    node->print_on(st);
+    node->print_on(st, scale);
   }
 }
 
@@ -2979,6 +3041,7 @@
              p2i(this), p2i(chunk), chunk->word_size(), list_count);
     ResourceMark rm;
     locked_print_free_chunks(&ls);
+    ls.cr(); // ~LogStream does not autoflush.
   }
 
   return chunk;
@@ -3073,80 +3136,10 @@
   _humongous_dictionary.report_statistics(out);
 }
 
-void ChunkManager::locked_get_statistics(ChunkManagerStatistics* stat) const {
-  assert_lock_strong(MetaspaceExpand_lock);
-  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
-    stat->num_by_type[i] = num_free_chunks(i);
-    stat->single_size_by_type[i] = size_by_index(i);
-    stat->total_size_by_type[i] = size_free_chunks_in_bytes(i);
-  }
-  stat->num_humongous_chunks = num_free_chunks(HumongousIndex);
-  stat->total_size_humongous_chunks = size_free_chunks_in_bytes(HumongousIndex);
-}
-
-void ChunkManager::get_statistics(ChunkManagerStatistics* stat) const {
-  MutexLockerEx cl(MetaspaceExpand_lock,
-                   Mutex::_no_safepoint_check_flag);
-  locked_get_statistics(stat);
-}
-
-void ChunkManager::print_statistics(const ChunkManagerStatistics* stat, outputStream* out, size_t scale) {
-  size_t total = 0;
-  assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
-
-  const char* unit = scale_unit(scale);
-  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
-    out->print("  " SIZE_FORMAT " %s (" SIZE_FORMAT " bytes) chunks, total ",
-                   stat->num_by_type[i], chunk_size_name(i),
-                   stat->single_size_by_type[i]);
-    if (scale == 1) {
-      out->print_cr(SIZE_FORMAT " bytes", stat->total_size_by_type[i]);
-    } else {
-      out->print_cr("%.2f%s", (float)stat->total_size_by_type[i] / scale, unit);
-    }
-
-    total += stat->total_size_by_type[i];
-  }
-
-
-  total += stat->total_size_humongous_chunks;
-
-  if (scale == 1) {
-    out->print_cr("  " SIZE_FORMAT " humongous chunks, total " SIZE_FORMAT " bytes",
-    stat->num_humongous_chunks, stat->total_size_humongous_chunks);
-
-    out->print_cr("  total size: " SIZE_FORMAT " bytes.", total);
-  } else {
-    out->print_cr("  " SIZE_FORMAT " humongous chunks, total %.2f%s",
-    stat->num_humongous_chunks,
-    (float)stat->total_size_humongous_chunks / scale, unit);
-
-    out->print_cr("  total size: %.2f%s.", (float)total / scale, unit);
-  }
-
-}
-
-void ChunkManager::print_all_chunkmanagers(outputStream* out, size_t scale) {
-  assert(scale == 1 || scale == K || scale == M || scale == G, "Invalid scale");
-
-  // Note: keep lock protection only to retrieving statistics; keep printing
-  // out of lock protection
-  ChunkManagerStatistics stat;
-  out->print_cr("Chunkmanager (non-class):");
-  const ChunkManager* const non_class_cm = Metaspace::chunk_manager_metadata();
-  if (non_class_cm != NULL) {
-    non_class_cm->get_statistics(&stat);
-    ChunkManager::print_statistics(&stat, out, scale);
-  } else {
-    out->print_cr("unavailable.");
-  }
-  out->print_cr("Chunkmanager (class):");
-  const ChunkManager* const class_cm = Metaspace::chunk_manager_class();
-  if (class_cm != NULL) {
-    class_cm->get_statistics(&stat);
-    ChunkManager::print_statistics(&stat, out, scale);
-  } else {
-    out->print_cr("unavailable.");
+void ChunkManager::collect_statistics(ChunkManagerStatistics* out) const {
+  MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    out->chunk_stats(i).add(num_free_chunks(i), size_free_chunks_in_bytes(i) / sizeof(MetaWord));
   }
 }
 
@@ -3202,77 +3195,6 @@
   return adjusted;
 }
 
-size_t SpaceManager::sum_free_in_chunks_in_use() const {
-  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
-  size_t free = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
-    Metachunk* chunk = chunks_in_use(i);
-    while (chunk != NULL) {
-      free += chunk->free_word_size();
-      chunk = chunk->next();
-    }
-  }
-  return free;
-}
-
-size_t SpaceManager::sum_waste_in_chunks_in_use() const {
-  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
-  size_t result = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
-   result += sum_waste_in_chunks_in_use(i);
-  }
-
-  return result;
-}
-
-size_t SpaceManager::sum_waste_in_chunks_in_use(ChunkIndex index) const {
-  size_t result = 0;
-  Metachunk* chunk = chunks_in_use(index);
-  // Count the free space in all the chunk but not the
-  // current chunk from which allocations are still being done.
-  while (chunk != NULL) {
-    if (chunk != current_chunk()) {
-      result += chunk->free_word_size();
-    }
-    chunk = chunk->next();
-  }
-  return result;
-}
-
-size_t SpaceManager::sum_capacity_in_chunks_in_use() const {
-  // For CMS use "allocated_chunks_words()" which does not need the
-  // Metaspace lock.  For the other collectors sum over the
-  // lists.  Use both methods as a check that "allocated_chunks_words()"
-  // is correct.  That is, sum_capacity_in_chunks() is too expensive
-  // to use in the product and allocated_chunks_words() should be used
-  // but allow for  checking that allocated_chunks_words() returns the same
-  // value as sum_capacity_in_chunks_in_use() which is the definitive
-  // answer.
-  if (UseConcMarkSweepGC) {
-    return allocated_chunks_words();
-  } else {
-    MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
-    size_t sum = 0;
-    for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
-      Metachunk* chunk = chunks_in_use(i);
-      while (chunk != NULL) {
-        sum += chunk->word_size();
-        chunk = chunk->next();
-      }
-    }
-  return sum;
-  }
-}
-
-size_t SpaceManager::sum_count_in_chunks_in_use() {
-  size_t count = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
-    count = count + sum_count_in_chunks_in_use(i);
-  }
-
-  return count;
-}
-
 size_t SpaceManager::sum_count_in_chunks_in_use(ChunkIndex i) {
   size_t count = 0;
   Metachunk* chunk = chunks_in_use(i);
@@ -3283,20 +3205,6 @@
   return count;
 }
 
-
-size_t SpaceManager::sum_used_in_chunks_in_use() const {
-  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
-  size_t used = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
-    Metachunk* chunk = chunks_in_use(i);
-    while (chunk != NULL) {
-      used += chunk->used_word_size();
-      chunk = chunk->next();
-    }
-  }
-  return used;
-}
-
 void SpaceManager::locked_print_chunks_in_use_on(outputStream* st) const {
 
   for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
@@ -3428,24 +3336,9 @@
 }
 
 void SpaceManager::print_on(outputStream* st) const {
-
-  for (ChunkIndex i = ZeroIndex;
-       i < NumberOfInUseLists ;
-       i = next_chunk_index(i) ) {
-    st->print_cr("  chunks_in_use " PTR_FORMAT " chunk size " SIZE_FORMAT,
-                 p2i(chunks_in_use(i)),
-                 chunks_in_use(i) == NULL ? 0 : chunks_in_use(i)->word_size());
-  }
-  st->print_cr("    waste:  Small " SIZE_FORMAT " Medium " SIZE_FORMAT
-               " Humongous " SIZE_FORMAT,
-               sum_waste_in_chunks_in_use(SmallIndex),
-               sum_waste_in_chunks_in_use(MediumIndex),
-               sum_waste_in_chunks_in_use(HumongousIndex));
-  // block free lists
-  if (block_freelists() != NULL) {
-    st->print_cr("total in block free lists " SIZE_FORMAT,
-      block_freelists()->total_size());
-  }
+  SpaceManagerStatistics stat;
+  add_to_statistics(&stat); // will lock _lock.
+  stat.print_on(st, 1*K, false);
 }
 
 SpaceManager::SpaceManager(Metaspace::MetadataType mdtype,
@@ -3453,43 +3346,46 @@
                            Mutex* lock) :
   _mdtype(mdtype),
   _space_type(space_type),
-  _allocated_blocks_words(0),
-  _allocated_chunks_words(0),
-  _allocated_chunks_count(0),
+  _capacity_words(0),
+  _used_words(0),
+  _overhead_words(0),
   _block_freelists(NULL),
   _lock(lock)
 {
   initialize();
 }
 
-void SpaceManager::inc_size_metrics(size_t words) {
+void SpaceManager::account_for_new_chunk(const Metachunk* new_chunk) {
+
   assert_lock_strong(MetaspaceExpand_lock);
-  // Total of allocated Metachunks and allocated Metachunks count
-  // for each SpaceManager
-  _allocated_chunks_words = _allocated_chunks_words + words;
-  _allocated_chunks_count++;
-  // Global total of capacity in allocated Metachunks
-  MetaspaceUtils::inc_capacity(mdtype(), words);
-  // Global total of allocated Metablocks.
-  // used_words_slow() includes the overhead in each
-  // Metachunk so include it in the used when the
-  // Metachunk is first added (so only added once per
-  // Metachunk).
-  MetaspaceUtils::inc_used(mdtype(), Metachunk::overhead());
+
+  _capacity_words += new_chunk->word_size();
+  _overhead_words += Metachunk::overhead();
+
+  // Adjust global counters:
+  MetaspaceUtils::inc_capacity(mdtype(), new_chunk->word_size());
+  MetaspaceUtils::inc_overhead(mdtype(), Metachunk::overhead());
 }
 
-void SpaceManager::inc_used_metrics(size_t words) {
-  // Add to the per SpaceManager total
-  Atomic::add(words, &_allocated_blocks_words);
-  // Add to the global total
+void SpaceManager::account_for_allocation(size_t words) {
+  // Note: we should be locked with the ClassloaderData-specific metaspace lock.
+  // We may or may not be locked with the global metaspace expansion lock.
+  assert_lock_strong(lock());
+
+  // Add to the per SpaceManager totals. This can be done non-atomically.
+  _used_words += words;
+
+  // Adjust global counters. This will be done atomically.
   MetaspaceUtils::inc_used(mdtype(), words);
 }
 
-void SpaceManager::dec_total_from_size_metrics() {
-  MetaspaceUtils::dec_capacity(mdtype(), allocated_chunks_words());
-  MetaspaceUtils::dec_used(mdtype(), allocated_blocks_words());
-  // Also deduct the overhead per Metachunk
-  MetaspaceUtils::dec_used(mdtype(), allocated_chunks_count() * Metachunk::overhead());
+void SpaceManager::account_for_spacemanager_death() {
+
+  assert_lock_strong(MetaspaceExpand_lock);
+
+  MetaspaceUtils::dec_capacity(mdtype(), _capacity_words);
+  MetaspaceUtils::dec_overhead(mdtype(), _overhead_words);
+  MetaspaceUtils::dec_used(mdtype(), _used_words);
 }
 
 void SpaceManager::initialize() {
@@ -3502,23 +3398,16 @@
 }
 
 SpaceManager::~SpaceManager() {
+
   // This call this->_lock which can't be done while holding MetaspaceExpand_lock
-  assert(sum_capacity_in_chunks_in_use() == allocated_chunks_words(),
-         "sum_capacity_in_chunks_in_use() " SIZE_FORMAT
-         " allocated_chunks_words() " SIZE_FORMAT,
-         sum_capacity_in_chunks_in_use(), allocated_chunks_words());
+  DEBUG_ONLY(verify_metrics());
 
   MutexLockerEx fcl(MetaspaceExpand_lock,
                     Mutex::_no_safepoint_check_flag);
 
-  assert(sum_count_in_chunks_in_use() == allocated_chunks_count(),
-         "sum_count_in_chunks_in_use() " SIZE_FORMAT
-         " allocated_chunks_count() " SIZE_FORMAT,
-         sum_count_in_chunks_in_use(), allocated_chunks_count());
-
   chunk_manager()->slow_locked_verify();
 
-  dec_total_from_size_metrics();
+  account_for_spacemanager_death();
 
   Log(gc, metaspace, freelist) log;
   if (log.is_trace()) {
@@ -3529,6 +3418,7 @@
     if (block_freelists() != NULL) {
       block_freelists()->print_on(&ls);
     }
+    ls.cr(); // ~LogStream does not autoflush.
   }
 
   // Add all the chunks in use by this space manager
@@ -3551,7 +3441,7 @@
 }
 
 void SpaceManager::deallocate(MetaWord* p, size_t word_size) {
-  assert_lock_strong(_lock);
+  assert_lock_strong(lock());
   // Allocations and deallocations are in raw_word_size
   size_t raw_word_size = get_allocation_word_size(word_size);
   // Lazily create a block_freelist
@@ -3559,6 +3449,7 @@
     _block_freelists = new BlockFreelist();
   }
   block_freelists()->return_block(p, raw_word_size);
+  DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_deallocs));
 }
 
 // Adds a chunk to the list of chunks in use.
@@ -3585,17 +3476,18 @@
   new_chunk->set_next(chunks_in_use(index));
   set_chunks_in_use(index, new_chunk);
 
-  // Add to the running sum of capacity
-  inc_size_metrics(new_chunk->word_size());
+  // Adjust counters.
+  account_for_new_chunk(new_chunk);
 
   assert(new_chunk->is_empty(), "Not ready for reuse");
   Log(gc, metaspace, freelist) log;
   if (log.is_trace()) {
-    log.trace("SpaceManager::add_chunk: " SIZE_FORMAT ") ", sum_count_in_chunks_in_use());
+    log.trace("SpaceManager::added chunk: ");
     ResourceMark rm;
     LogStream ls(log.trace());
     new_chunk->print_on(&ls);
     chunk_manager()->locked_print_free_chunks(&ls);
+    ls.cr(); // ~LogStream does not autoflush.
   }
 }
 
@@ -3605,7 +3497,7 @@
     if (remaining_words >= SmallBlocks::small_block_min_size()) {
       MetaWord* ptr = current_chunk()->allocate(remaining_words);
       deallocate(ptr, remaining_words);
-      inc_used_metrics(remaining_words);
+      account_for_allocation(remaining_words);
     }
   }
 }
@@ -3633,6 +3525,9 @@
   size_t raw_word_size = get_allocation_word_size(word_size);
   BlockFreelist* fl =  block_freelists();
   MetaWord* p = NULL;
+
+  DEBUG_ONLY(if (VerifyMetaspace) verify_metrics_locked());
+
   // Allocation from the dictionary is expensive in the sense that
   // the dictionary has to be searched for a size.  Don't allocate
   // from the dictionary until it starts to get fat.  Is this
@@ -3640,6 +3535,9 @@
   // for allocations.  Do some profiling.  JJJ
   if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) {
     p = fl->get_block(raw_word_size);
+    if (p != NULL) {
+      DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs_from_deallocated_blocks));
+    }
   }
   if (p == NULL) {
     p = allocate_work(raw_word_size);
@@ -3651,7 +3549,7 @@
 // Returns the address of spaced allocated for "word_size".
 // This methods does not know about blocks (Metablocks)
 MetaWord* SpaceManager::allocate_work(size_t word_size) {
-  assert_lock_strong(_lock);
+  assert_lock_strong(lock());
 #ifdef ASSERT
   if (Metadebug::test_metadata_failure()) {
     return NULL;
@@ -3669,7 +3567,7 @@
   }
 
   if (result != NULL) {
-    inc_used_metrics(word_size);
+    account_for_allocation(word_size);
     assert(result != (MetaWord*) chunks_in_use(MediumIndex),
            "Head of the list is being allocated");
   }
@@ -3697,162 +3595,129 @@
   return;
 }
 
-#ifdef ASSERT
-void SpaceManager::verify_allocated_blocks_words() {
-  // Verification is only guaranteed at a safepoint.
-  assert(SafepointSynchronize::is_at_safepoint() || !Universe::is_fully_initialized(),
-    "Verification can fail if the applications is running");
-  assert(allocated_blocks_words() == sum_used_in_chunks_in_use(),
-         "allocation total is not consistent " SIZE_FORMAT
-         " vs " SIZE_FORMAT,
-         allocated_blocks_words(), sum_used_in_chunks_in_use());
+void SpaceManager::add_to_statistics_locked(SpaceManagerStatistics* out) const {
+  assert_lock_strong(lock());
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    UsedChunksStatistics& chunk_stat = out->chunk_stats(i);
+    Metachunk* chunk = chunks_in_use(i);
+    while (chunk != NULL) {
+      chunk_stat.add_num(1);
+      chunk_stat.add_cap(chunk->word_size());
+      chunk_stat.add_overhead(Metachunk::overhead());
+      chunk_stat.add_used(chunk->used_word_size() - Metachunk::overhead());
+      if (chunk != current_chunk()) {
+        chunk_stat.add_waste(chunk->free_word_size());
+      } else {
+        chunk_stat.add_free(chunk->free_word_size());
+      }
+      chunk = chunk->next();
+    }
+  }
+  if (block_freelists() != NULL) {
+    out->add_free_blocks_info(block_freelists()->num_blocks(), block_freelists()->total_size());
+  }
 }
 
-#endif
-
-void SpaceManager::dump(outputStream* const out) const {
-  size_t curr_total = 0;
-  size_t waste = 0;
-  uint i = 0;
-  size_t used = 0;
-  size_t capacity = 0;
-
-  // Add up statistics for all chunks in this SpaceManager.
-  for (ChunkIndex index = ZeroIndex;
-       index < NumberOfInUseLists;
-       index = next_chunk_index(index)) {
-    for (Metachunk* curr = chunks_in_use(index);
-         curr != NULL;
-         curr = curr->next()) {
-      out->print("%d) ", i++);
-      curr->print_on(out);
-      curr_total += curr->word_size();
-      used += curr->used_word_size();
-      capacity += curr->word_size();
-      waste += curr->free_word_size() + curr->overhead();;
-    }
-  }
-
-  if (log_is_enabled(Trace, gc, metaspace, freelist)) {
-    if (block_freelists() != NULL) block_freelists()->print_on(out);
-  }
-
-  size_t free = current_chunk() == NULL ? 0 : current_chunk()->free_word_size();
-  // Free space isn't wasted.
-  waste -= free;
-
-  out->print_cr("total of all chunks "  SIZE_FORMAT " used " SIZE_FORMAT
-                " free " SIZE_FORMAT " capacity " SIZE_FORMAT
-                " waste " SIZE_FORMAT, curr_total, used, free, capacity, waste);
+void SpaceManager::add_to_statistics(SpaceManagerStatistics* out) const {
+  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
+  add_to_statistics_locked(out);
 }
 
+#ifdef ASSERT
+void SpaceManager::verify_metrics_locked() const {
+  assert_lock_strong(lock());
+
+  SpaceManagerStatistics stat;
+  add_to_statistics_locked(&stat);
+
+  UsedChunksStatistics chunk_stats = stat.totals();
+
+  DEBUG_ONLY(chunk_stats.check_sanity());
+
+  assert_counter(_capacity_words, chunk_stats.cap(), "SpaceManager::_capacity_words");
+  assert_counter(_used_words, chunk_stats.used(), "SpaceManager::_used_words");
+  assert_counter(_overhead_words, chunk_stats.overhead(), "SpaceManager::_overhead_words");
+}
+
+void SpaceManager::verify_metrics() const {
+  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
+  verify_metrics_locked();
+}
+#endif // ASSERT
+
+
+
 // MetaspaceUtils
-
-
-size_t MetaspaceUtils::_capacity_words[] = {0, 0};
-volatile size_t MetaspaceUtils::_used_words[] = {0, 0};
-
-size_t MetaspaceUtils::free_bytes(Metaspace::MetadataType mdtype) {
+size_t MetaspaceUtils::_capacity_words [Metaspace:: MetadataTypeCount] = {0, 0};
+size_t MetaspaceUtils::_overhead_words [Metaspace:: MetadataTypeCount] = {0, 0};
+volatile size_t MetaspaceUtils::_used_words [Metaspace:: MetadataTypeCount] = {0, 0};
+
+// Collect used metaspace statistics. This involves walking the CLDG. The resulting
+// output will be the accumulated values for all live metaspaces.
+// Note: method does not do any locking.
+void MetaspaceUtils::collect_statistics(ClassLoaderMetaspaceStatistics* out) {
+  out->reset();
+  ClassLoaderDataGraphMetaspaceIterator iter;
+   while (iter.repeat()) {
+     ClassLoaderMetaspace* msp = iter.get_next();
+     if (msp != NULL) {
+       msp->add_to_statistics(out);
+     }
+   }
+}
+
+size_t MetaspaceUtils::free_in_vs_bytes(Metaspace::MetadataType mdtype) {
   VirtualSpaceList* list = Metaspace::get_space_list(mdtype);
   return list == NULL ? 0 : list->free_bytes();
 }
 
-size_t MetaspaceUtils::free_bytes() {
-  return free_bytes(Metaspace::ClassType) + free_bytes(Metaspace::NonClassType);
+size_t MetaspaceUtils::free_in_vs_bytes() {
+  return free_in_vs_bytes(Metaspace::ClassType) + free_in_vs_bytes(Metaspace::NonClassType);
+}
+
+static void inc_stat_nonatomically(size_t* pstat, size_t words) {
+  assert_lock_strong(MetaspaceExpand_lock);
+  (*pstat) += words;
+}
+
+static void dec_stat_nonatomically(size_t* pstat, size_t words) {
+  assert_lock_strong(MetaspaceExpand_lock);
+  const size_t size_now = *pstat;
+  assert(size_now >= words, "About to decrement counter below zero "
+         "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".",
+         size_now, words);
+  *pstat = size_now - words;
+}
+
+static void inc_stat_atomically(volatile size_t* pstat, size_t words) {
+  Atomic::add(words, pstat);
+}
+
+static void dec_stat_atomically(volatile size_t* pstat, size_t words) {
+  const size_t size_now = *pstat;
+  assert(size_now >= words, "About to decrement counter below zero "
+         "(current value: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ".",
+         size_now, words);
+  Atomic::sub(words, pstat);
 }
 
 void MetaspaceUtils::dec_capacity(Metaspace::MetadataType mdtype, size_t words) {
-  assert_lock_strong(MetaspaceExpand_lock);
-  assert(words <= capacity_words(mdtype),
-         "About to decrement below 0: words " SIZE_FORMAT
-         " is greater than _capacity_words[%u] " SIZE_FORMAT,
-         words, mdtype, capacity_words(mdtype));
-  _capacity_words[mdtype] -= words;
+  dec_stat_nonatomically(&_capacity_words[mdtype], words);
 }
-
 void MetaspaceUtils::inc_capacity(Metaspace::MetadataType mdtype, size_t words) {
-  assert_lock_strong(MetaspaceExpand_lock);
-  // Needs to be atomic
-  _capacity_words[mdtype] += words;
+  inc_stat_nonatomically(&_capacity_words[mdtype], words);
 }
-
 void MetaspaceUtils::dec_used(Metaspace::MetadataType mdtype, size_t words) {
-  assert(words <= used_words(mdtype),
-         "About to decrement below 0: words " SIZE_FORMAT
-         " is greater than _used_words[%u] " SIZE_FORMAT,
-         words, mdtype, used_words(mdtype));
-  // For CMS deallocation of the Metaspaces occurs during the
-  // sweep which is a concurrent phase.  Protection by the MetaspaceExpand_lock
-  // is not enough since allocation is on a per Metaspace basis
-  // and protected by the Metaspace lock.
-  Atomic::sub(words, &_used_words[mdtype]);
-}
-
-void MetaspaceUtils::inc_used(Metaspace::MetadataType mdtype, size_t words) {
-  // _used_words tracks allocations for
-  // each piece of metadata.  Those allocations are
-  // generally done concurrently by different application
-  // threads so must be done atomically.
-  Atomic::add(words, &_used_words[mdtype]);
-}
-
-size_t MetaspaceUtils::used_bytes_slow(Metaspace::MetadataType mdtype) {
-  size_t used = 0;
-  ClassLoaderDataGraphMetaspaceIterator iter;
-  while (iter.repeat()) {
-    ClassLoaderMetaspace* msp = iter.get_next();
-    // Sum allocated_blocks_words for each metaspace
-    if (msp != NULL) {
-      used += msp->used_words_slow(mdtype);
-    }
-  }
-  return used * BytesPerWord;
+  dec_stat_atomically(&_used_words[mdtype], words);
 }
-
-size_t MetaspaceUtils::free_bytes_slow(Metaspace::MetadataType mdtype) {
-  size_t free = 0;
-  ClassLoaderDataGraphMetaspaceIterator iter;
-  while (iter.repeat()) {
-    ClassLoaderMetaspace* msp = iter.get_next();
-    if (msp != NULL) {
-      free += msp->free_words_slow(mdtype);
-    }
-  }
-  return free * BytesPerWord;
+void MetaspaceUtils::inc_used(Metaspace::MetadataType mdtype, size_t words) {
+  inc_stat_atomically(&_used_words[mdtype], words);
 }
-
-size_t MetaspaceUtils::capacity_bytes_slow(Metaspace::MetadataType mdtype) {
-  if ((mdtype == Metaspace::ClassType) && !Metaspace::using_class_space()) {
-    return 0;
-  }
-  // Don't count the space in the freelists.  That space will be
-  // added to the capacity calculation as needed.
-  size_t capacity = 0;
-  ClassLoaderDataGraphMetaspaceIterator iter;
-  while (iter.repeat()) {
-    ClassLoaderMetaspace* msp = iter.get_next();
-    if (msp != NULL) {
-      capacity += msp->capacity_words_slow(mdtype);
-    }
-  }
-  return capacity * BytesPerWord;
+void MetaspaceUtils::dec_overhead(Metaspace::MetadataType mdtype, size_t words) {
+  dec_stat_nonatomically(&_overhead_words[mdtype], words);
 }
-
-size_t MetaspaceUtils::capacity_bytes_slow() {
-#ifdef PRODUCT
-  // Use capacity_bytes() in PRODUCT instead of this function.
-  guarantee(false, "Should not call capacity_bytes_slow() in the PRODUCT");
-#endif
-  size_t class_capacity = capacity_bytes_slow(Metaspace::ClassType);
-  size_t non_class_capacity = capacity_bytes_slow(Metaspace::NonClassType);
-  assert(capacity_bytes() == class_capacity + non_class_capacity,
-         "bad accounting: capacity_bytes() " SIZE_FORMAT
-         " class_capacity + non_class_capacity " SIZE_FORMAT
-         " class_capacity " SIZE_FORMAT " non_class_capacity " SIZE_FORMAT,
-         capacity_bytes(), class_capacity + non_class_capacity,
-         class_capacity, non_class_capacity);
-
-  return class_capacity + non_class_capacity;
+void MetaspaceUtils::inc_overhead(Metaspace::MetadataType mdtype, size_t words) {
+  inc_stat_nonatomically(&_overhead_words[mdtype], words);
 }
 
 size_t MetaspaceUtils::reserved_bytes(Metaspace::MetadataType mdtype) {
@@ -3934,280 +3799,386 @@
   }
 }
 
-// Print information for class space and data space separately.
-// This is almost the same as above.
-void MetaspaceUtils::print_on(outputStream* out, Metaspace::MetadataType mdtype) {
-  size_t free_chunks_capacity_bytes = free_chunks_total_bytes(mdtype);
-  size_t capacity_bytes = capacity_bytes_slow(mdtype);
-  size_t used_bytes = used_bytes_slow(mdtype);
-  size_t free_bytes = free_bytes_slow(mdtype);
-  size_t used_and_free = used_bytes + free_bytes +
-                           free_chunks_capacity_bytes;
-  out->print_cr("  Chunk accounting: (used in chunks " SIZE_FORMAT
-             "K + unused in chunks " SIZE_FORMAT "K  + "
-             " capacity in free chunks " SIZE_FORMAT "K) = " SIZE_FORMAT
-             "K  capacity in allocated chunks " SIZE_FORMAT "K",
-             used_bytes / K,
-             free_bytes / K,
-             free_chunks_capacity_bytes / K,
-             used_and_free / K,
-             capacity_bytes / K);
-  // Accounting can only be correct if we got the values during a safepoint
-  assert(!SafepointSynchronize::is_at_safepoint() || used_and_free == capacity_bytes, "Accounting is wrong");
-}
-
-// Print total fragmentation for class metaspaces
-void MetaspaceUtils::print_class_waste(outputStream* out) {
-  assert(Metaspace::using_class_space(), "class metaspace not used");
-  size_t cls_specialized_waste = 0, cls_small_waste = 0, cls_medium_waste = 0;
-  size_t cls_specialized_count = 0, cls_small_count = 0, cls_medium_count = 0, cls_humongous_count = 0;
-  ClassLoaderDataGraphMetaspaceIterator iter;
-  while (iter.repeat()) {
-    ClassLoaderMetaspace* msp = iter.get_next();
-    if (msp != NULL) {
-      cls_specialized_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(SpecializedIndex);
-      cls_specialized_count += msp->class_vsm()->sum_count_in_chunks_in_use(SpecializedIndex);
-      cls_small_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(SmallIndex);
-      cls_small_count += msp->class_vsm()->sum_count_in_chunks_in_use(SmallIndex);
-      cls_medium_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(MediumIndex);
-      cls_medium_count += msp->class_vsm()->sum_count_in_chunks_in_use(MediumIndex);
-      cls_humongous_count += msp->class_vsm()->sum_count_in_chunks_in_use(HumongousIndex);
-    }
-  }
-  out->print_cr(" class: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", "
-                SIZE_FORMAT " small(s) " SIZE_FORMAT ", "
-                SIZE_FORMAT " medium(s) " SIZE_FORMAT ", "
-                "large count " SIZE_FORMAT,
-                cls_specialized_count, cls_specialized_waste,
-                cls_small_count, cls_small_waste,
-                cls_medium_count, cls_medium_waste, cls_humongous_count);
-}
-
-// Print total fragmentation for data and class metaspaces separately
-void MetaspaceUtils::print_waste(outputStream* out) {
-  size_t specialized_waste = 0, small_waste = 0, medium_waste = 0;
-  size_t specialized_count = 0, small_count = 0, medium_count = 0, humongous_count = 0;
-
-  ClassLoaderDataGraphMetaspaceIterator iter;
-  while (iter.repeat()) {
-    ClassLoaderMetaspace* msp = iter.get_next();
-    if (msp != NULL) {
-      specialized_waste += msp->vsm()->sum_waste_in_chunks_in_use(SpecializedIndex);
-      specialized_count += msp->vsm()->sum_count_in_chunks_in_use(SpecializedIndex);
-      small_waste += msp->vsm()->sum_waste_in_chunks_in_use(SmallIndex);
-      small_count += msp->vsm()->sum_count_in_chunks_in_use(SmallIndex);
-      medium_waste += msp->vsm()->sum_waste_in_chunks_in_use(MediumIndex);
-      medium_count += msp->vsm()->sum_count_in_chunks_in_use(MediumIndex);
-      humongous_count += msp->vsm()->sum_count_in_chunks_in_use(HumongousIndex);
-    }
-  }
-  out->print_cr("Total fragmentation waste (words) doesn't count free space");
-  out->print_cr("  data: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", "
-                        SIZE_FORMAT " small(s) " SIZE_FORMAT ", "
-                        SIZE_FORMAT " medium(s) " SIZE_FORMAT ", "
-                        "large count " SIZE_FORMAT,
-             specialized_count, specialized_waste, small_count,
-             small_waste, medium_count, medium_waste, humongous_count);
-  if (Metaspace::using_class_space()) {
-    print_class_waste(out);
-  }
-}
-
-class MetadataStats {
+class PrintCLDMetaspaceInfoClosure : public CLDClosure {
 private:
-  size_t _capacity;
-  size_t _used;
-  size_t _free;
-  size_t _waste;
+  outputStream* const _out;
+  const size_t        _scale;
+  const bool          _do_print;
+  const bool          _break_down_by_chunktype;
 
 public:
-  MetadataStats() : _capacity(0), _used(0), _free(0), _waste(0) { }
-  MetadataStats(size_t capacity, size_t used, size_t free, size_t waste)
-  : _capacity(capacity), _used(used), _free(free), _waste(waste) { }
-
-  void add(const MetadataStats& stats) {
-    _capacity += stats.capacity();
-    _used += stats.used();
-    _free += stats.free();
-    _waste += stats.waste();
-  }
-
-  size_t capacity() const { return _capacity; }
-  size_t used() const     { return _used; }
-  size_t free() const     { return _free; }
-  size_t waste() const    { return _waste; }
-
-  void print_on(outputStream* out, size_t scale) const;
-};
-
-
-void MetadataStats::print_on(outputStream* out, size_t scale) const {
-  const char* unit = scale_unit(scale);
-  out->print_cr("capacity=%10.2f%s used=%10.2f%s free=%10.2f%s waste=%10.2f%s",
-    (float)capacity() / scale, unit,
-    (float)used() / scale, unit,
-    (float)free() / scale, unit,
-    (float)waste() / scale, unit);
-}
-
-class PrintCLDMetaspaceInfoClosure : public CLDClosure {
-private:
-  outputStream*  _out;
-  size_t         _scale;
-
-  size_t         _total_count;
-  MetadataStats  _total_metadata;
-  MetadataStats  _total_class;
-
-  size_t         _total_anon_count;
-  MetadataStats  _total_anon_metadata;
-  MetadataStats  _total_anon_class;
+
+  uintx                           _num_loaders;
+  ClassLoaderMetaspaceStatistics  _stats_total;
+
+  uintx                           _num_loaders_by_spacetype [Metaspace::MetaspaceTypeCount];
+  ClassLoaderMetaspaceStatistics  _stats_by_spacetype [Metaspace::MetaspaceTypeCount];
 
 public:
-  PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale = K)
-  : _out(out), _scale(scale), _total_count(0), _total_anon_count(0) { }
-
-  ~PrintCLDMetaspaceInfoClosure() {
-    print_summary();
+  PrintCLDMetaspaceInfoClosure(outputStream* out, size_t scale, bool do_print, bool break_down_by_chunktype)
+    : _out(out), _scale(scale), _do_print(do_print), _break_down_by_chunktype(break_down_by_chunktype)
+    , _num_loaders(0)
+  {
+    memset(_num_loaders_by_spacetype, 0, sizeof(_num_loaders_by_spacetype));
   }
 
   void do_cld(ClassLoaderData* cld) {
+
     assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 
-    if (cld->is_unloading()) return;
     ClassLoaderMetaspace* msp = cld->metaspace_or_null();
     if (msp == NULL) {
       return;
     }
 
-    bool anonymous = false;
-    if (cld->is_anonymous()) {
-      _out->print_cr("ClassLoader: for anonymous class");
-      anonymous = true;
-    } else {
-      ResourceMark rm;
-      _out->print_cr("ClassLoader: %s", cld->loader_name());
+    // Collect statistics for this class loader metaspace
+    ClassLoaderMetaspaceStatistics this_cld_stat;
+    msp->add_to_statistics(&this_cld_stat);
+
+    // And add it to the running totals
+    _stats_total.add(this_cld_stat);
+    _num_loaders ++;
+    _stats_by_spacetype[msp->space_type()].add(this_cld_stat);
+    _num_loaders_by_spacetype[msp->space_type()] ++;
+
+    // Optionally, print.
+    if (_do_print) {
+
+      _out->print(UINTX_FORMAT_W(4) ": ", _num_loaders);
+
+      if (cld->is_anonymous()) {
+        _out->print("ClassLoaderData " PTR_FORMAT " for anonymous class", p2i(cld));
+      } else {
+        ResourceMark rm;
+        _out->print("ClassLoaderData " PTR_FORMAT " for %s", p2i(cld), cld->loader_name());
+      }
+
+      if (cld->is_unloading()) {
+        _out->print(" (unloading)");
+      }
+
+      this_cld_stat.print_on(_out, _scale, _break_down_by_chunktype);
+      _out->cr();
+
     }
 
-    print_metaspace(msp, anonymous);
-    _out->cr();
-  }
-
-private:
-  void print_metaspace(ClassLoaderMetaspace* msp, bool anonymous);
-  void print_summary() const;
+  } // do_cld
+
 };
 
-void PrintCLDMetaspaceInfoClosure::print_metaspace(ClassLoaderMetaspace* msp, bool anonymous){
-  assert(msp != NULL, "Sanity");
-  SpaceManager* vsm = msp->vsm();
-  const char* unit = scale_unit(_scale);
-
-  size_t capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
-  size_t used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
-  size_t free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
-  size_t waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
-
-  _total_count ++;
-  MetadataStats metadata_stats(capacity, used, free, waste);
-  _total_metadata.add(metadata_stats);
-
-  if (anonymous) {
-    _total_anon_count ++;
-    _total_anon_metadata.add(metadata_stats);
-  }
-
-  _out->print("  Metadata   ");
-  metadata_stats.print_on(_out, _scale);
+void MetaspaceUtils::print_vs(outputStream* out, size_t scale) {
+  const size_t reserved_nonclass_words = reserved_bytes(Metaspace::NonClassType) / sizeof(MetaWord);
+  const size_t committed_nonclass_words = committed_bytes(Metaspace::NonClassType) / sizeof(MetaWord);
+  {
+    if (Metaspace::using_class_space()) {
+      out->print("  Non-class space:  ");
+    }
+    print_scaled_words(out, reserved_nonclass_words, scale, 7);
+    out->print(" reserved, ");
+    print_scaled_words_and_percentage(out, committed_nonclass_words, reserved_nonclass_words, scale, 7);
+    out->print_cr(" committed ");
+
+    if (Metaspace::using_class_space()) {
+      const size_t reserved_class_words = reserved_bytes(Metaspace::ClassType) / sizeof(MetaWord);
+      const size_t committed_class_words = committed_bytes(Metaspace::ClassType) / sizeof(MetaWord);
+      out->print("      Class space:  ");
+      print_scaled_words(out, reserved_class_words, scale, 7);
+      out->print(" reserved, ");
+      print_scaled_words_and_percentage(out, committed_class_words, reserved_class_words, scale, 7);
+      out->print_cr(" committed ");
+
+      const size_t reserved_words = reserved_nonclass_words + reserved_class_words;
+      const size_t committed_words = committed_nonclass_words + committed_class_words;
+      out->print("             Both:  ");
+      print_scaled_words(out, reserved_words, scale, 7);
+      out->print(" reserved, ");
+      print_scaled_words_and_percentage(out, committed_words, reserved_words, scale, 7);
+      out->print_cr(" committed ");
+    }
+  }
+}
+
+// This will print out a basic metaspace usage report but
+// unlike print_report() is guaranteed not to lock or to walk the CLDG.
+void MetaspaceUtils::print_basic_report(outputStream* out, size_t scale) {
+
+  out->cr();
+  out->print_cr("Usage:");
 
   if (Metaspace::using_class_space()) {
-    vsm = msp->class_vsm();
-
-    capacity = vsm->sum_capacity_in_chunks_in_use() * BytesPerWord;
-    used = vsm->sum_used_in_chunks_in_use() * BytesPerWord;
-    free = vsm->sum_free_in_chunks_in_use() * BytesPerWord;
-    waste = vsm->sum_waste_in_chunks_in_use() * BytesPerWord;
-
-    MetadataStats class_stats(capacity, used, free, waste);
-    _total_class.add(class_stats);
-
-    if (anonymous) {
-      _total_anon_class.add(class_stats);
-    }
-
-    _out->print("  Class data ");
-    class_stats.print_on(_out, _scale);
-  }
-}
-
-void PrintCLDMetaspaceInfoClosure::print_summary() const {
-  const char* unit = scale_unit(_scale);
-  _out->cr();
-  _out->print_cr("Summary:");
-
-  MetadataStats total;
-  total.add(_total_metadata);
-  total.add(_total_class);
-
-  _out->print("  Total class loaders=" SIZE_FORMAT_W(6) " ", _total_count);
-  total.print_on(_out, _scale);
-
-  _out->print("                    Metadata ");
-  _total_metadata.print_on(_out, _scale);
+    out->print("  Non-class:  ");
+  }
+
+  // In its most basic form, we do not require walking the CLDG. Instead, just print the running totals from
+  // MetaspaceUtils.
+  const size_t cap_nc = MetaspaceUtils::capacity_words(Metaspace::NonClassType);
+  const size_t overhead_nc = MetaspaceUtils::overhead_words(Metaspace::NonClassType);
+  const size_t used_nc = MetaspaceUtils::used_words(Metaspace::NonClassType);
+  const size_t free_and_waste_nc = cap_nc - overhead_nc - used_nc;
+
+  print_scaled_words(out, cap_nc, scale, 5);
+  out->print(" capacity, ");
+  print_scaled_words_and_percentage(out, used_nc, cap_nc, scale, 5);
+  out->print(" used, ");
+  print_scaled_words_and_percentage(out, free_and_waste_nc, cap_nc, scale, 5);
+  out->print(" free+waste, ");
+  print_scaled_words_and_percentage(out, overhead_nc, cap_nc, scale, 5);
+  out->print(" overhead. ");
+  out->cr();
 
   if (Metaspace::using_class_space()) {
-    _out->print("                  Class data ");
-    _total_class.print_on(_out, _scale);
-  }
-  _out->cr();
-
-  MetadataStats total_anon;
-  total_anon.add(_total_anon_metadata);
-  total_anon.add(_total_anon_class);
-
-  _out->print("For anonymous classes=" SIZE_FORMAT_W(6) " ", _total_anon_count);
-  total_anon.print_on(_out, _scale);
-
-  _out->print("                    Metadata ");
-  _total_anon_metadata.print_on(_out, _scale);
-
-  if (Metaspace::using_class_space()) {
-    _out->print("                  Class data ");
-    _total_anon_class.print_on(_out, _scale);
-  }
-}
-
-void MetaspaceUtils::print_metadata_for_nmt(outputStream* out, size_t scale) {
-  const char* unit = scale_unit(scale);
-  out->print_cr("Metaspaces:");
-  out->print_cr("  Metadata space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
-    reserved_bytes(Metaspace::NonClassType) / scale, unit,
-    committed_bytes(Metaspace::NonClassType) / scale, unit);
-  if (Metaspace::using_class_space()) {
-    out->print_cr("  Class    space: reserved=" SIZE_FORMAT_W(10) "%s committed=" SIZE_FORMAT_W(10) "%s",
-    reserved_bytes(Metaspace::ClassType) / scale, unit,
-    committed_bytes(Metaspace::ClassType) / scale, unit);
+    const size_t cap_c = MetaspaceUtils::capacity_words(Metaspace::ClassType);
+    const size_t overhead_c = MetaspaceUtils::overhead_words(Metaspace::ClassType);
+    const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType);
+    const size_t free_and_waste_c = cap_c - overhead_c - used_c;
+    out->print("      Class:  ");
+    print_scaled_words(out, cap_c, scale, 5);
+    out->print(" capacity, ");
+    print_scaled_words_and_percentage(out, used_c, cap_c, scale, 5);
+    out->print(" used, ");
+    print_scaled_words_and_percentage(out, free_and_waste_c, cap_c, scale, 5);
+    out->print(" free+waste, ");
+    print_scaled_words_and_percentage(out, overhead_c, cap_c, scale, 5);
+    out->print(" overhead. ");
+    out->cr();
+
+    out->print("       Both:  ");
+    const size_t cap = cap_nc + cap_c;
+
+    print_scaled_words(out, cap, scale, 5);
+    out->print(" capacity, ");
+    print_scaled_words_and_percentage(out, used_nc + used_c, cap, scale, 5);
+    out->print(" used, ");
+    print_scaled_words_and_percentage(out, free_and_waste_nc + free_and_waste_c, cap, scale, 5);
+    out->print(" free+waste, ");
+    print_scaled_words_and_percentage(out, overhead_nc + overhead_c, cap, scale, 5);
+    out->print(" overhead. ");
+    out->cr();
   }
 
   out->cr();
-  ChunkManager::print_all_chunkmanagers(out, scale);
+  out->print_cr("Virtual space:");
+
+  print_vs(out, scale);
 
   out->cr();
-  out->print_cr("Per-classloader metadata:");
+  out->print_cr("Chunk freelists:");
+
+  if (Metaspace::using_class_space()) {
+    out->print("   Non-Class:  ");
+  }
+  print_human_readable_size(out, Metaspace::chunk_manager_metadata()->free_chunks_total_words(), scale);
   out->cr();
-
-  PrintCLDMetaspaceInfoClosure cl(out, scale);
-  ClassLoaderDataGraph::cld_do(&cl);
+  if (Metaspace::using_class_space()) {
+    out->print("       Class:  ");
+    print_human_readable_size(out, Metaspace::chunk_manager_class()->free_chunks_total_words(), scale);
+    out->cr();
+    out->print("        Both:  ");
+    print_human_readable_size(out, Metaspace::chunk_manager_class()->free_chunks_total_words() +
+                              Metaspace::chunk_manager_metadata()->free_chunks_total_words(), scale);
+    out->cr();
+  }
+  out->cr();
+
 }
 
-
-// Dump global metaspace things from the end of ClassLoaderDataGraph
-void MetaspaceUtils::dump(outputStream* out) {
-  out->print_cr("All Metaspace:");
-  out->print("data space: "); print_on(out, Metaspace::NonClassType);
-  out->print("class space: "); print_on(out, Metaspace::ClassType);
-  print_waste(out);
-}
+void MetaspaceUtils::print_report(outputStream* out, size_t scale, int flags) {
+
+  const bool print_loaders = (flags & rf_show_loaders) > 0;
+  const bool print_by_chunktype = (flags & rf_break_down_by_chunktype) > 0;
+  const bool print_by_spacetype = (flags & rf_break_down_by_spacetype) > 0;
+
+  // Some report options require walking the class loader data graph.
+  PrintCLDMetaspaceInfoClosure cl(out, scale, print_loaders, print_by_chunktype);
+  if (print_loaders) {
+    out->cr();
+    out->print_cr("Usage per loader:");
+    out->cr();
+  }
+
+  ClassLoaderDataGraph::cld_do(&cl); // collect data and optionally print
+
+  // Print totals, broken up by space type.
+  if (print_by_spacetype) {
+    out->cr();
+    out->print_cr("Usage per space type:");
+    out->cr();
+    for (int space_type = (int)Metaspace::ZeroMetaspaceType;
+         space_type < (int)Metaspace::MetaspaceTypeCount; space_type ++)
+    {
+      uintx num = cl._num_loaders_by_spacetype[space_type];
+      out->print("%s (" UINTX_FORMAT " loader%s)%c",
+        space_type_name((Metaspace::MetaspaceType)space_type),
+        num, (num == 1 ? "" : "s"), (num > 0 ? ':' : '.'));
+      if (num > 0) {
+        cl._stats_by_spacetype[space_type].print_on(out, scale, print_by_chunktype);
+      }
+      out->cr();
+    }
+  }
+
+  // Print totals for in-use data:
+  out->cr();
+  out->print_cr("Total Usage ( " UINTX_FORMAT " loader%s)%c",
+      cl._num_loaders, (cl._num_loaders == 1 ? "" : "s"), (cl._num_loaders > 0 ? ':' : '.'));
+
+  cl._stats_total.print_on(out, scale, print_by_chunktype);
+
+  // -- Print Virtual space.
+  out->cr();
+  out->print_cr("Virtual space:");
+
+  print_vs(out, scale);
+
+  // -- Print VirtualSpaceList details.
+  if ((flags & rf_show_vslist) > 0) {
+    out->cr();
+    out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : "");
+
+    if (Metaspace::using_class_space()) {
+      out->print_cr("   Non-Class:");
+    }
+    Metaspace::space_list()->print_on(out, scale);
+    if (Metaspace::using_class_space()) {
+      out->print_cr("       Class:");
+      Metaspace::class_space_list()->print_on(out, scale);
+    }
+  }
+  out->cr();
+
+  // -- Print VirtualSpaceList map.
+  if ((flags & rf_show_vsmap) > 0) {
+    out->cr();
+    out->print_cr("Virtual space map:");
+
+    if (Metaspace::using_class_space()) {
+      out->print_cr("   Non-Class:");
+    }
+    Metaspace::space_list()->print_map(out);
+    if (Metaspace::using_class_space()) {
+      out->print_cr("       Class:");
+      Metaspace::class_space_list()->print_map(out);
+    }
+  }
+  out->cr();
+
+  // -- Print Freelists (ChunkManager) details
+  out->cr();
+  out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : "");
+
+  ChunkManagerStatistics non_class_cm_stat;
+  Metaspace::chunk_manager_metadata()->collect_statistics(&non_class_cm_stat);
+
+  if (Metaspace::using_class_space()) {
+    out->print_cr("   Non-Class:");
+  }
+  non_class_cm_stat.print_on(out, scale);
+
+  if (Metaspace::using_class_space()) {
+    ChunkManagerStatistics class_cm_stat;
+    Metaspace::chunk_manager_class()->collect_statistics(&class_cm_stat);
+    out->print_cr("       Class:");
+    class_cm_stat.print_on(out, scale);
+  }
+
+  // As a convenience, print a summary of common waste.
+  out->cr();
+  out->print("Waste ");
+  // For all wastages, print percentages from total. As total use the total size of memory committed for metaspace.
+  const size_t committed_words = committed_bytes() / BytesPerWord;
+
+  out->print("(percentages refer to total committed size ");
+  print_scaled_words(out, committed_words, scale);
+  out->print_cr("):");
+
+  // Print space committed but not yet used by any class loader
+  const size_t unused_words_in_vs = MetaspaceUtils::free_in_vs_bytes() / BytesPerWord;
+  out->print("              Committed unused: ");
+  print_scaled_words_and_percentage(out, unused_words_in_vs, committed_words, scale, 6);
+  out->cr();
+
+  // Print waste for in-use chunks.
+  UsedChunksStatistics ucs_nonclass = cl._stats_total.nonclass_sm_stats().totals();
+  UsedChunksStatistics ucs_class = cl._stats_total.class_sm_stats().totals();
+  UsedChunksStatistics ucs_all;
+  ucs_all.add(ucs_nonclass);
+  ucs_all.add(ucs_class);
+
+  out->print("        Waste in chunks in use: ");
+  print_scaled_words_and_percentage(out, ucs_all.waste(), committed_words, scale, 6);
+  out->cr();
+  out->print("         Free in chunks in use: ");
+  print_scaled_words_and_percentage(out, ucs_all.free(), committed_words, scale, 6);
+  out->cr();
+  out->print("     Overhead in chunks in use: ");
+  print_scaled_words_and_percentage(out, ucs_all.overhead(), committed_words, scale, 6);
+  out->cr();
+
+  // Print waste in free chunks.
+  const size_t total_capacity_in_free_chunks =
+      Metaspace::chunk_manager_metadata()->free_chunks_total_words() +
+     (Metaspace::using_class_space() ? Metaspace::chunk_manager_class()->free_chunks_total_words() : 0);
+  out->print("                In free chunks: ");
+  print_scaled_words_and_percentage(out, total_capacity_in_free_chunks, committed_words, scale, 6);
+  out->cr();
+
+  // Print waste in deallocated blocks.
+  const uintx free_blocks_num =
+      cl._stats_total.nonclass_sm_stats().free_blocks_num() +
+      cl._stats_total.class_sm_stats().free_blocks_num();
+  const size_t free_blocks_cap_words =
+      cl._stats_total.nonclass_sm_stats().free_blocks_cap_words() +
+      cl._stats_total.class_sm_stats().free_blocks_cap_words();
+  out->print("Deallocated from chunks in use: ");
+  print_scaled_words_and_percentage(out, free_blocks_cap_words, committed_words, scale, 6);
+  out->print(" ("UINTX_FORMAT " blocks)", free_blocks_num);
+  out->cr();
+
+  // Print total waste.
+  const size_t total_waste = ucs_all.waste() + ucs_all.free() + ucs_all.overhead() + total_capacity_in_free_chunks
+      + free_blocks_cap_words + unused_words_in_vs;
+  out->print("                       -total-: ");
+  print_scaled_words_and_percentage(out, total_waste, committed_words, scale, 6);
+  out->cr();
+
+  // Print internal statistics
+#ifdef ASSERT
+  out->cr();
+  out->cr();
+  out->print_cr("Internal statistics:");
+  out->cr();
+  out->print_cr("Number of allocations: " UINTX_FORMAT ".", g_internal_statistics.num_allocs);
+  out->print_cr("Number of space births: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_births);
+  out->print_cr("Number of space deaths: " UINTX_FORMAT ".", g_internal_statistics.num_metaspace_deaths);
+  out->print_cr("Number of virtual space node births: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_created);
+  out->print_cr("Number of virtual space node deaths: " UINTX_FORMAT ".", g_internal_statistics.num_vsnodes_purged);
+  out->print_cr("Number of times virtual space nodes were expanded: " UINTX_FORMAT ".", g_internal_statistics.num_committed_space_expanded);
+  out->print_cr("Number of deallocations: " UINTX_FORMAT " (" UINTX_FORMAT " external).", g_internal_statistics.num_deallocs, g_internal_statistics.num_external_deallocs);
+  out->print_cr("Allocations from deallocated blocks: " UINTX_FORMAT ".", g_internal_statistics.num_allocs_from_deallocated_blocks);
+  out->cr();
+#endif
+
+  // Print some interesting settings
+  out->cr();
+  out->cr();
+  out->print("MaxMetaspaceSize: ");
+  print_human_readable_size(out, MaxMetaspaceSize, scale);
+  out->cr();
+  out->print("InitialBootClassLoaderMetaspaceSize: ");
+  print_human_readable_size(out, InitialBootClassLoaderMetaspaceSize, scale);
+  out->cr();
+
+  out->print("UseCompressedClassPointers: %s", UseCompressedClassPointers ? "true" : "false");
+  out->cr();
+  if (Metaspace::using_class_space()) {
+    out->print("CompressedClassSpaceSize: ");
+    print_human_readable_size(out, CompressedClassSpaceSize, scale);
+  }
+
+  out->cr();
+  out->cr();
+
+} // MetaspaceUtils::print_report()
 
 // Prints an ASCII representation of the given space.
 void MetaspaceUtils::print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype) {
@@ -4241,53 +4212,37 @@
   }
 }
 
-void MetaspaceUtils::verify_capacity() {
-#ifdef ASSERT
-  size_t running_sum_capacity_bytes = capacity_bytes();
-  // For purposes of the running sum of capacity, verify against capacity
-  size_t capacity_in_use_bytes = capacity_bytes_slow();
-  assert(running_sum_capacity_bytes == capacity_in_use_bytes,
-         "capacity_words() * BytesPerWord " SIZE_FORMAT
-         " capacity_bytes_slow()" SIZE_FORMAT,
-         running_sum_capacity_bytes, capacity_in_use_bytes);
-  for (Metaspace::MetadataType i = Metaspace::ClassType;
-       i < Metaspace:: MetadataTypeCount;
-       i = (Metaspace::MetadataType)(i + 1)) {
-    size_t capacity_in_use_bytes = capacity_bytes_slow(i);
-    assert(capacity_bytes(i) == capacity_in_use_bytes,
-           "capacity_bytes(%u) " SIZE_FORMAT
-           " capacity_bytes_slow(%u)" SIZE_FORMAT,
-           i, capacity_bytes(i), i, capacity_in_use_bytes);
-  }
-#endif
-}
-
-void MetaspaceUtils::verify_used() {
+void MetaspaceUtils::verify_metrics() {
 #ifdef ASSERT
-  size_t running_sum_used_bytes = used_bytes();
-  // For purposes of the running sum of used, verify against used
-  size_t used_in_use_bytes = used_bytes_slow();
-  assert(used_bytes() == used_in_use_bytes,
-         "used_bytes() " SIZE_FORMAT
-         " used_bytes_slow()" SIZE_FORMAT,
-         used_bytes(), used_in_use_bytes);
-  for (Metaspace::MetadataType i = Metaspace::ClassType;
-       i < Metaspace:: MetadataTypeCount;
-       i = (Metaspace::MetadataType)(i + 1)) {
-    size_t used_in_use_bytes = used_bytes_slow(i);
-    assert(used_bytes(i) == used_in_use_bytes,
-           "used_bytes(%u) " SIZE_FORMAT
-           " used_bytes_slow(%u)" SIZE_FORMAT,
-           i, used_bytes(i), i, used_in_use_bytes);
-  }
+  // Please note: there are time windows where the internal counters are out of sync with
+  // reality. For example, when a newly created ClassLoaderMetaspace creates its first chunk -
+  // the ClassLoaderMetaspace is not yet attached to its ClassLoaderData object and hence will
+  // not be counted when iterating the CLDG. So be careful when you call this method.
+  ClassLoaderMetaspaceStatistics total_stat;
+  collect_statistics(&total_stat);
+  UsedChunksStatistics nonclass_chunk_stat = total_stat.nonclass_sm_stats().totals();
+  UsedChunksStatistics class_chunk_stat = total_stat.class_sm_stats().totals();
+
+  bool mismatch = false;
+  for (int i = 0; i < Metaspace::MetadataTypeCount; i ++) {
+    Metaspace::MetadataType mdtype = (Metaspace::MetadataType)i;
+    UsedChunksStatistics chunk_stat = total_stat.sm_stats(mdtype).totals();
+    if (capacity_words(mdtype) != chunk_stat.cap() ||
+        used_words(mdtype) != chunk_stat.used() ||
+        overhead_words(mdtype) != chunk_stat.overhead()) {
+      mismatch = true;
+      tty->print_cr("MetaspaceUtils::verify_metrics: counter mismatch for mdtype=%u:", mdtype);
+      tty->print_cr("Expected cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".",
+                    capacity_words(mdtype), used_words(mdtype), overhead_words(mdtype));
+      tty->print_cr("Got cap " SIZE_FORMAT ", used " SIZE_FORMAT ", overhead " SIZE_FORMAT ".",
+                    chunk_stat.cap(), chunk_stat.used(), chunk_stat.overhead());
+      tty->flush();
+    }
+  }
+  assert(mismatch == false, "MetaspaceUtils::verify_metrics: counter mismatch.");
 #endif
 }
 
-void MetaspaceUtils::verify_metrics() {
-  verify_capacity();
-  verify_used();
-}
-
 
 // Metaspace methods
 
@@ -4486,6 +4441,7 @@
     ResourceMark rm;
     LogStream ls(lt);
     print_compressed_class_space(&ls, requested_addr);
+    ls.cr(); // ~LogStream does not autoflush.
   }
 }
 
@@ -4707,12 +4663,13 @@
       if (loader_data->metaspace_or_null() != NULL) {
         LogStream ls(log.debug());
         loader_data->print_value_on(&ls);
+        ls.cr(); // ~LogStream does not autoflush.
       }
     }
     LogStream ls(log.info());
-    MetaspaceUtils::dump(&ls);
-    MetaspaceUtils::print_metaspace_map(&ls, mdtype);
-    ChunkManager::print_all_chunkmanagers(&ls);
+    // In case of an OOM, log out a short but still useful report.
+    MetaspaceUtils::print_basic_report(&ls, 0);
+    ls.cr(); // ~LogStream does not autoflush.
   }
 
   bool out_of_compressed_class_space = false;
@@ -4787,16 +4744,23 @@
 
 // ClassLoaderMetaspace
 
-ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type) {
+ClassLoaderMetaspace::ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type)
+  : _lock(lock)
+  , _space_type(type)
+  , _vsm(NULL)
+  , _class_vsm(NULL)
+{
   initialize(lock, type);
 }
 
 ClassLoaderMetaspace::~ClassLoaderMetaspace() {
+  DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_deaths));
   delete _vsm;
   if (Metaspace::using_class_space()) {
     delete _class_vsm;
   }
 }
+
 void ClassLoaderMetaspace::initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype) {
   Metachunk* chunk = get_initialization_chunk(type, mdtype);
   if (chunk != NULL) {
@@ -4822,6 +4786,8 @@
 void ClassLoaderMetaspace::initialize(Mutex* lock, Metaspace::MetaspaceType type) {
   Metaspace::verify_global_initialization();
 
+  DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_metaspace_births));
+
   // Allocate SpaceManager for metadata objects.
   _vsm = new SpaceManager(Metaspace::NonClassType, type, lock);
 
@@ -4843,6 +4809,9 @@
 
 MetaWord* ClassLoaderMetaspace::allocate(size_t word_size, Metaspace::MetadataType mdtype) {
   Metaspace::assert_not_frozen();
+
+  DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs));
+
   // Don't use class_vsm() unless UseCompressedClassPointers is true.
   if (Metaspace::is_class_space_allocation(mdtype)) {
     return  class_vsm()->allocate(word_size);
@@ -4878,52 +4847,14 @@
   return res;
 }
 
-size_t ClassLoaderMetaspace::used_words_slow(Metaspace::MetadataType mdtype) const {
-  if (mdtype == Metaspace::ClassType) {
-    return Metaspace::using_class_space() ? class_vsm()->sum_used_in_chunks_in_use() : 0;
-  } else {
-    return vsm()->sum_used_in_chunks_in_use();  // includes overhead!
-  }
-}
-
-size_t ClassLoaderMetaspace::free_words_slow(Metaspace::MetadataType mdtype) const {
-  Metaspace::assert_not_frozen();
-  if (mdtype == Metaspace::ClassType) {
-    return Metaspace::using_class_space() ? class_vsm()->sum_free_in_chunks_in_use() : 0;
-  } else {
-    return vsm()->sum_free_in_chunks_in_use();
-  }
-}
-
-// Space capacity in the Metaspace.  It includes
-// space in the list of chunks from which allocations
-// have been made. Don't include space in the global freelist and
-// in the space available in the dictionary which
-// is already counted in some chunk.
-size_t ClassLoaderMetaspace::capacity_words_slow(Metaspace::MetadataType mdtype) const {
-  if (mdtype == Metaspace::ClassType) {
-    return Metaspace::using_class_space() ? class_vsm()->sum_capacity_in_chunks_in_use() : 0;
-  } else {
-    return vsm()->sum_capacity_in_chunks_in_use();
-  }
-}
-
-size_t ClassLoaderMetaspace::used_bytes_slow(Metaspace::MetadataType mdtype) const {
-  return used_words_slow(mdtype) * BytesPerWord;
-}
-
-size_t ClassLoaderMetaspace::capacity_bytes_slow(Metaspace::MetadataType mdtype) const {
-  return capacity_words_slow(mdtype) * BytesPerWord;
-}
-
 size_t ClassLoaderMetaspace::allocated_blocks_bytes() const {
-  return vsm()->allocated_blocks_bytes() +
-      (Metaspace::using_class_space() ? class_vsm()->allocated_blocks_bytes() : 0);
+  return (vsm()->used_words() +
+      (Metaspace::using_class_space() ? class_vsm()->used_words() : 0)) * BytesPerWord;
 }
 
 size_t ClassLoaderMetaspace::allocated_chunks_bytes() const {
-  return vsm()->allocated_chunks_bytes() +
-      (Metaspace::using_class_space() ? class_vsm()->allocated_chunks_bytes() : 0);
+  return (vsm()->capacity_words() +
+      (Metaspace::using_class_space() ? class_vsm()->capacity_words() : 0)) * BytesPerWord;
 }
 
 void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) {
@@ -4931,6 +4862,8 @@
   assert(!SafepointSynchronize::is_at_safepoint()
          || Thread::current()->is_VM_thread(), "should be the VM thread");
 
+  DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_external_deallocs));
+
   MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag);
 
   if (is_class && Metaspace::using_class_space()) {
@@ -4962,16 +4895,18 @@
   }
 }
 
-void ClassLoaderMetaspace::dump(outputStream* const out) const {
-  out->print_cr("\nVirtual space manager: " INTPTR_FORMAT, p2i(vsm()));
-  vsm()->dump(out);
+void ClassLoaderMetaspace::add_to_statistics_locked(ClassLoaderMetaspaceStatistics* out) const {
+  assert_lock_strong(lock());
+  vsm()->add_to_statistics_locked(&out->nonclass_sm_stats());
   if (Metaspace::using_class_space()) {
-    out->print_cr("\nClass space manager: " INTPTR_FORMAT, p2i(class_vsm()));
-    class_vsm()->dump(out);
+    class_vsm()->add_to_statistics_locked(&out->class_sm_stats());
   }
 }
 
-
+void ClassLoaderMetaspace::add_to_statistics(ClassLoaderMetaspaceStatistics* out) const {
+  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);
+  add_to_statistics_locked(out);
+}
 
 #ifdef ASSERT
 static void do_verify_chunk(Metachunk* chunk) {
@@ -5317,12 +5252,12 @@
 
 extern void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out) {
   ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(mdType);
-  ChunkManager::ChunkManagerStatistics stat;
-  chunk_manager->get_statistics(&stat);
-  out->num_specialized_chunks = (int)stat.num_by_type[SpecializedIndex];
-  out->num_small_chunks = (int)stat.num_by_type[SmallIndex];
-  out->num_medium_chunks = (int)stat.num_by_type[MediumIndex];
-  out->num_humongous_chunks = (int)stat.num_humongous_chunks;
+  ChunkManagerStatistics stat;
+  chunk_manager->collect_statistics(&stat);
+  out->num_specialized_chunks = (int)stat.chunk_stats(SpecializedIndex).num();
+  out->num_small_chunks = (int)stat.chunk_stats(SmallIndex).num();
+  out->num_medium_chunks = (int)stat.chunk_stats(MediumIndex).num();
+  out->num_humongous_chunks = (int)stat.chunk_stats(HumongousIndex).num();
 }
 
 struct chunk_geometry_t {
--- a/src/hotspot/share/memory/metaspace.hpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/memory/metaspace.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -68,6 +68,11 @@
 class VirtualSpaceList;
 class CollectedHeap;
 
+namespace metaspace {
+namespace internals {
+  class ClassLoaderMetaspaceStatistics;
+}}
+
 // Metaspaces each have a  SpaceManager and allocations
 // are done by the SpaceManager.  Allocations are done
 // out of the current Metachunk.  When the current Metachunk
@@ -94,10 +99,12 @@
     MetadataTypeCount
   };
   enum MetaspaceType {
-    StandardMetaspaceType,
-    BootMetaspaceType,
-    AnonymousMetaspaceType,
-    ReflectionMetaspaceType
+    ZeroMetaspaceType = 0,
+    StandardMetaspaceType = ZeroMetaspaceType,
+    BootMetaspaceType = StandardMetaspaceType + 1,
+    AnonymousMetaspaceType = BootMetaspaceType + 1,
+    ReflectionMetaspaceType = AnonymousMetaspaceType + 1,
+    MetaspaceTypeCount
   };
 
  private:
@@ -193,7 +200,6 @@
 
   static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
                             MetaspaceObj::Type type, TRAPS);
-  void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
 
   static bool contains(const void* ptr);
   static bool contains_non_shared(const void* ptr);
@@ -238,96 +244,97 @@
   void initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
   Metachunk* get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
 
+  const Metaspace::MetaspaceType _space_type;
+  Mutex* const  _lock;
   SpaceManager* _vsm;
+  SpaceManager* _class_vsm;
+
   SpaceManager* vsm() const { return _vsm; }
-
-  SpaceManager* _class_vsm;
   SpaceManager* class_vsm() const { return _class_vsm; }
   SpaceManager* get_space_manager(Metaspace::MetadataType mdtype) {
     assert(mdtype != Metaspace::MetadataTypeCount, "MetadaTypeCount can't be used as mdtype");
     return mdtype == Metaspace::ClassType ? class_vsm() : vsm();
   }
 
+  Mutex* lock() const { return _lock; }
+
   MetaWord* expand_and_allocate(size_t size, Metaspace::MetadataType mdtype);
 
   size_t class_chunk_size(size_t word_size);
 
+  // Adds to the given statistic object. Must be locked with CLD metaspace lock.
+  void add_to_statistics_locked(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const;
+
  public:
 
   ClassLoaderMetaspace(Mutex* lock, Metaspace::MetaspaceType type);
   ~ClassLoaderMetaspace();
 
+  Metaspace::MetaspaceType space_type() const { return _space_type; }
+
   // Allocate space for metadata of type mdtype. This is space
   // within a Metachunk and is used by
   //   allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS)
   MetaWord* allocate(size_t word_size, Metaspace::MetadataType mdtype);
 
-  size_t used_words_slow(Metaspace::MetadataType mdtype) const;
-  size_t free_words_slow(Metaspace::MetadataType mdtype) const;
-  size_t capacity_words_slow(Metaspace::MetadataType mdtype) const;
-
-  size_t used_bytes_slow(Metaspace::MetadataType mdtype) const;
-  size_t capacity_bytes_slow(Metaspace::MetadataType mdtype) const;
-
   size_t allocated_blocks_bytes() const;
   size_t allocated_chunks_bytes() const;
 
   void deallocate(MetaWord* ptr, size_t byte_size, bool is_class);
 
-  void dump(outputStream* const out) const;
-
   void print_on(outputStream* st) const;
   // Debugging support
   void verify();
 
+  // Adds to the given statistic object. Will lock with CLD metaspace lock.
+  void add_to_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out) const;
+
 }; // ClassLoaderMetaspace
 
+class MetaspaceUtils : AllStatic {
 
-class MetaspaceUtils : AllStatic {
+  // Spacemanager updates running counters.
+  friend class SpaceManager;
+
+  // Running counters for statistics concerning in-use chunks.
+  // Note: capacity = used + free + waste + overhead. Note that we do not
+  // count free and waste. Their sum can be deduces from the three other values.
+  // For more details, one should call print_report() from within a safe point.
+  static size_t _capacity_words [Metaspace:: MetadataTypeCount];
+  static size_t _overhead_words [Metaspace:: MetadataTypeCount];
+  static volatile size_t _used_words [Metaspace:: MetadataTypeCount];
+
+  // Atomically decrement or increment in-use statistic counters
+  static void dec_capacity(Metaspace::MetadataType mdtype, size_t words);
+  static void inc_capacity(Metaspace::MetadataType mdtype, size_t words);
+  static void dec_used(Metaspace::MetadataType mdtype, size_t words);
+  static void inc_used(Metaspace::MetadataType mdtype, size_t words);
+  static void dec_overhead(Metaspace::MetadataType mdtype, size_t words);
+  static void inc_overhead(Metaspace::MetadataType mdtype, size_t words);
+
+
+  // Getters for the in-use counters.
+  static size_t capacity_words(Metaspace::MetadataType mdtype)        { return _capacity_words[mdtype]; }
+  static size_t used_words(Metaspace::MetadataType mdtype)            { return _used_words[mdtype]; }
+  static size_t overhead_words(Metaspace::MetadataType mdtype)        { return _overhead_words[mdtype]; }
+
   static size_t free_chunks_total_words(Metaspace::MetadataType mdtype);
 
-  // These methods iterate over the classloader data graph
-  // for the given Metaspace type.  These are slow.
-  static size_t used_bytes_slow(Metaspace::MetadataType mdtype);
-  static size_t free_bytes_slow(Metaspace::MetadataType mdtype);
-  static size_t capacity_bytes_slow(Metaspace::MetadataType mdtype);
-  static size_t capacity_bytes_slow();
+  // Helper for print_xx_report.
+  static void print_vs(outputStream* out, size_t scale);
 
-  // Running sum of space in all Metachunks that has been
-  // allocated to a Metaspace.  This is used instead of
-  // iterating over all the classloaders. One for each
-  // type of Metadata
-  static size_t _capacity_words[Metaspace:: MetadataTypeCount];
-  // Running sum of space in all Metachunks that
-  // are being used for metadata. One for each
-  // type of Metadata.
-  static volatile size_t _used_words[Metaspace:: MetadataTypeCount];
+public:
 
- public:
-  // Decrement and increment _allocated_capacity_words
-  static void dec_capacity(Metaspace::MetadataType type, size_t words);
-  static void inc_capacity(Metaspace::MetadataType type, size_t words);
-
-  // Decrement and increment _allocated_used_words
-  static void dec_used(Metaspace::MetadataType type, size_t words);
-  static void inc_used(Metaspace::MetadataType type, size_t words);
-
-  // Total of space allocated to metadata in all Metaspaces.
-  // This sums the space used in each Metachunk by
-  // iterating over the classloader data graph
-  static size_t used_bytes_slow() {
-    return used_bytes_slow(Metaspace::ClassType) +
-           used_bytes_slow(Metaspace::NonClassType);
-  }
+  // Collect used metaspace statistics. This involves walking the CLDG. The resulting
+  // output will be the accumulated values for all live metaspaces.
+  // Note: method does not do any locking.
+  static void collect_statistics(metaspace::internals::ClassLoaderMetaspaceStatistics* out);
 
   // Used by MetaspaceCounters
   static size_t free_chunks_total_words();
   static size_t free_chunks_total_bytes();
   static size_t free_chunks_total_bytes(Metaspace::MetadataType mdtype);
 
-  static size_t capacity_words(Metaspace::MetadataType mdtype) {
-    return _capacity_words[mdtype];
-  }
   static size_t capacity_words() {
     return capacity_words(Metaspace::NonClassType) +
            capacity_words(Metaspace::ClassType);
@@ -339,9 +346,6 @@
     return capacity_words() * BytesPerWord;
   }
 
-  static size_t used_words(Metaspace::MetadataType mdtype) {
-    return _used_words[mdtype];
-  }
   static size_t used_words() {
     return used_words(Metaspace::NonClassType) +
            used_words(Metaspace::ClassType);
@@ -353,8 +357,9 @@
     return used_words() * BytesPerWord;
   }
 
-  static size_t free_bytes();
-  static size_t free_bytes(Metaspace::MetadataType mdtype);
+  // Space committed but yet unclaimed by any class loader.
+  static size_t free_in_vs_bytes();
+  static size_t free_in_vs_bytes(Metaspace::MetadataType mdtype);
 
   static size_t reserved_bytes(Metaspace::MetadataType mdtype);
   static size_t reserved_bytes() {
@@ -373,7 +378,29 @@
     return min_chunk_size_words() * BytesPerWord;
   }
 
-  static void print_metadata_for_nmt(outputStream* out, size_t scale = K);
+  // Flags for print_report().
+  enum ReportFlag {
+    // Show usage by class loader.
+    rf_show_loaders                 = (1 << 0),
+    // Breaks report down by chunk type (small, medium, ...).
+    rf_break_down_by_chunktype      = (1 << 1),
+    // Breaks report down by space type (anonymous, reflection, ...).
+    rf_break_down_by_spacetype      = (1 << 2),
+    // Print details about the underlying virtual spaces.
+    rf_show_vslist                  = (1 << 3),
+    // Print metaspace map.
+    rf_show_vsmap                   = (1 << 4)
+  };
+
+  // This will print out a basic metaspace usage report but
+  // unlike print_report() is guaranteed not to lock or to walk the CLDG.
+  static void print_basic_report(outputStream* st, size_t scale);
+
+  // Prints a report about the current metaspace state.
+  // Optional parts can be enabled via flags.
+  // Function will walk the CLDG and will lock the expand lock; if that is not
+  // convenient, use print_basic_report() instead.
+  static void print_report(outputStream* out, size_t scale = 0, int flags = 0);
 
   static bool has_chunk_free_list(Metaspace::MetadataType mdtype);
   static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype);
@@ -381,20 +408,13 @@
   // Print change in used metadata.
   static void print_metaspace_change(size_t prev_metadata_used);
   static void print_on(outputStream * out);
-  static void print_on(outputStream * out, Metaspace::MetadataType mdtype);
-
-  static void print_class_waste(outputStream* out);
-  static void print_waste(outputStream* out);
 
   // Prints an ASCII representation of the given space.
   static void print_metaspace_map(outputStream* out, Metaspace::MetadataType mdtype);
 
   static void dump(outputStream* out);
   static void verify_free_chunks();
-  // Checks that the values returned by allocated_capacity_bytes() and
-  // capacity_bytes_slow() are the same.
-  static void verify_capacity();
-  static void verify_used();
+  // Check internal counters (capacity, used).
   static void verify_metrics();
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, SAP 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.
+ *
+ */
+
+#include "precompiled.hpp"
+
+#include "memory/metaspace/metaspaceCommon.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+namespace metaspace {
+namespace internals {
+
+// Print a size, in words, scaled.
+void print_scaled_words(outputStream* st, size_t word_size, size_t scale, int width) {
+  print_human_readable_size(st, word_size * sizeof(MetaWord), scale, width);
+}
+
+// Convenience helper: prints a size value and a percentage.
+void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale, int width) {
+  print_scaled_words(st, word_size, scale, width);
+  st->print(" (");
+  print_percentage(st, compare_word_size, word_size);
+  st->print(")");
+}
+
+
+// Print a human readable size.
+// byte_size: size, in bytes, to be printed.
+// scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively,
+//         or 0, which means the best scale is choosen dynamically.
+// width: printing width.
+void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale, int width)  {
+  if (scale == 0) {
+    // Dynamic mode. Choose scale for this value.
+    if (byte_size == 0) {
+      // Zero values are printed as bytes.
+      scale = 1;
+    } else {
+      if (byte_size >= G) {
+        scale = G;
+      } else if (byte_size >= M) {
+        scale = M;
+      } else if (byte_size >= K) {
+        scale = K;
+      } else {
+        scale = 1;
+      }
+    }
+    return print_human_readable_size(st, byte_size, scale, width);
+  }
+
+#ifdef ASSERT
+  assert(scale == 1 || scale == BytesPerWord || scale == K || scale == M || scale == G, "Invalid scale");
+  // Special case: printing wordsize should only be done with word-sized values
+  if (scale == BytesPerWord) {
+    assert(byte_size % BytesPerWord == 0, "not word sized");
+  }
+#endif
+
+  if (scale == 1) {
+    st->print("%*" PRIuPTR " bytes", width, byte_size);
+  } else if (scale == BytesPerWord) {
+    st->print("%*" PRIuPTR " words", width, byte_size / BytesPerWord);
+  } else {
+    const char* display_unit = "";
+    switch(scale) {
+      case 1: display_unit = "bytes"; break;
+      case BytesPerWord: display_unit = "words"; break;
+      case K: display_unit = "KB"; break;
+      case M: display_unit = "MB"; break;
+      case G: display_unit = "GB"; break;
+      default:
+        ShouldNotReachHere();
+    }
+    float display_value = (float) byte_size / scale;
+    // Since we use width to display a number with two trailing digits, increase it a bit.
+    width += 3;
+    // Prevent very small but non-null values showing up as 0.00.
+    if (byte_size > 0 && display_value < 0.01f) {
+      st->print("%*s %s", width, "<0.01", display_unit);
+    } else {
+      st->print("%*.2f %s", width, display_value, display_unit);
+    }
+  }
+}
+
+// Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values
+// larger than 99% but not 100% are displayed as ">100%".
+void print_percentage(outputStream* st, size_t total, size_t part) {
+  if (total == 0) {
+    st->print("  ?%%");
+  } else if (part == 0) {
+    st->print("  0%%");
+  } else if (part == total) {
+    st->print("100%%");
+  } else {
+    // Note: clearly print very-small-but-not-0% and very-large-but-not-100% percentages.
+    float p = ((float)part / total) * 100.0f;
+    if (p < 1.0f) {
+      st->print(" <1%%");
+    } else if (p > 99.0f){
+      st->print(">99%%");
+    } else {
+      st->print("%3.0f%%", p);
+    }
+  }
+}
+
+} // namespace internals
+} // namespace metaspace
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+#ifndef SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP_
+#define SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP_
+
+#include "utilities/globalDefinitions.hpp"
+
+
+class outputStream;
+
+namespace metaspace {
+namespace internals {
+
+// Print a size, in words, scaled.
+void print_scaled_words(outputStream* st, size_t word_size, size_t scale = 0, int width = -1);
+
+// Convenience helper: prints a size value and a percentage.
+void print_scaled_words_and_percentage(outputStream* st, size_t word_size, size_t compare_word_size, size_t scale = 0, int width = -1);
+
+// Print a human readable size.
+// byte_size: size, in bytes, to be printed.
+// scale: one of 1 (byte-wise printing), sizeof(word) (word-size printing), K, M, G (scaled by KB, MB, GB respectively,
+//         or 0, which means the best scale is choosen dynamically.
+// width: printing width.
+void print_human_readable_size(outputStream* st, size_t byte_size, size_t scale = 0, int width = -1);
+
+// Prints a percentage value. Values smaller than 1% but not 0 are displayed as "<1%", values
+// larger than 99% but not 100% are displayed as ">100%".
+void print_percentage(outputStream* st, size_t total, size_t part);
+
+} // namespace internals
+} // namespace metaspace
+
+#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+#include "precompiled.hpp"
+#include "memory/metaspace.hpp"
+#include "memory/metaspace/metaspaceDCmd.hpp"
+#include "memory/resourceArea.hpp"
+#include "services/diagnosticCommand.hpp"
+#include "services/nmtCommon.hpp"
+
+namespace metaspace {
+
+MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap)
+  : DCmdWithParser(output, heap)
+  , _basic("basic", "Prints a basic summary (does not need a safepoint).", "BOOLEAN", false, "false")
+  , _show_loaders("show-loaders", "Shows usage by class loader.", "BOOLEAN", false, "false")
+  , _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false")
+  , _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false")
+  , _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false")
+  , _show_vsmap("vsmap", "Shows chunk composition of the underlying virtual spaces", "BOOLEAN", false, "false")
+  , _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) "
+           "or \"dynamic\" for a dynamically choosen scale.",
+     "STRING", false, "dynamic")
+{
+  _dcmdparser.add_dcmd_option(&_basic);
+  _dcmdparser.add_dcmd_option(&_show_loaders);
+  _dcmdparser.add_dcmd_option(&_by_chunktype);
+  _dcmdparser.add_dcmd_option(&_by_spacetype);
+  _dcmdparser.add_dcmd_option(&_show_vslist);
+  _dcmdparser.add_dcmd_option(&_show_vsmap);
+  _dcmdparser.add_dcmd_option(&_scale);
+}
+
+int MetaspaceDCmd::num_arguments() {
+  ResourceMark rm;
+  MetaspaceDCmd* dcmd = new MetaspaceDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+void MetaspaceDCmd::execute(DCmdSource source, TRAPS) {
+  // Parse scale value.
+  const char* scale_value = _scale.value();
+  size_t scale = 0;
+  if (scale_value != NULL) {
+    if (strcasecmp("dynamic", scale_value) == 0) {
+      scale = 0;
+    } else {
+      scale = NMTUtil::scale_from_name(scale_value);
+      if (scale == 0) {
+        output()->print_cr("Invalid scale: \"%s\". Will use dynamic scaling.", scale_value);
+      }
+    }
+  }
+  if (_basic.value() == true) {
+    if (_show_loaders.value() || _by_chunktype.value() || _by_spacetype.value() ||
+        _show_vslist.value() || _show_vsmap.value()) {
+      // Basic mode. Just print essentials. Does not need to be at a safepoint.
+      output()->print_cr("In basic mode, additional arguments are ignored.");
+    }
+    MetaspaceUtils::print_basic_report(output(), scale);
+  } else {
+    // Full mode. Requires safepoint.
+    int flags = 0;
+    if (_show_loaders.value())         flags |= MetaspaceUtils::rf_show_loaders;
+    if (_by_chunktype.value())         flags |= MetaspaceUtils::rf_break_down_by_chunktype;
+    if (_by_spacetype.value())         flags |= MetaspaceUtils::rf_break_down_by_spacetype;
+    if (_show_vslist.value())          flags |= MetaspaceUtils::rf_show_vslist;
+    if (_show_vsmap.value())           flags |= MetaspaceUtils::rf_show_vsmap;
+    VM_PrintMetadata op(output(), scale, flags);
+    VMThread::execute(&op);
+  }
+}
+
+} // namespace metaspace
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+#ifndef SHARE_MEMORY_METASPACE_METASPACEDCMD_HPP_
+#define SHARE_MEMORY_METASPACE_METASPACEDCMD_HPP_
+
+#include "services/diagnosticCommand.hpp"
+
+class outputStream;
+
+namespace metaspace {
+
+class MetaspaceDCmd : public DCmdWithParser {
+  DCmdArgument<bool> _basic;
+  DCmdArgument<bool> _show_loaders;
+  DCmdArgument<bool> _by_spacetype;
+  DCmdArgument<bool> _by_chunktype;
+  DCmdArgument<bool> _show_vslist;
+  DCmdArgument<bool> _show_vsmap;
+  DCmdArgument<char*> _scale;
+public:
+  MetaspaceDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "VM.metaspace";
+  }
+  static const char* description() {
+    return "Prints the statistics for the metaspace";
+  }
+  static const char* impact() {
+      return "Medium: Depends on number of classes loaded.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
+} // namespace metaspace
+
+#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, SAP 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.
+ *
+ */
+#include "precompiled.hpp"
+
+#include "memory/metachunk.hpp"
+#include "memory/metaspace/metaspaceCommon.hpp"
+#include "memory/metaspace/metaspaceStatistics.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+namespace metaspace {
+namespace internals {
+
+// FreeChunksStatistics methods
+
+FreeChunksStatistics::FreeChunksStatistics()
+: _num(0), _cap(0)
+{}
+
+void FreeChunksStatistics::reset() {
+  _num = 0; _cap = 0;
+}
+
+void FreeChunksStatistics::add(uintx n, size_t s) {
+  _num += n; _cap += s;
+}
+
+void FreeChunksStatistics::add(const FreeChunksStatistics& other) {
+  _num += other._num;
+  _cap += other._cap;
+}
+
+void FreeChunksStatistics::print_on(outputStream* st, size_t scale) const {
+  st->print(UINTX_FORMAT, _num);
+  st->print(" chunks, total capacity ");
+  print_scaled_words(st, _cap, scale);
+}
+
+// ChunkManagerStatistics methods
+
+void ChunkManagerStatistics::reset() {
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    _chunk_stats[i].reset();
+  }
+}
+
+size_t ChunkManagerStatistics::total_capacity() const {
+  return _chunk_stats[SpecializedIndex].cap() +
+      _chunk_stats[SmallIndex].cap() +
+      _chunk_stats[MediumIndex].cap() +
+      _chunk_stats[HumongousIndex].cap();
+}
+
+void ChunkManagerStatistics::print_on(outputStream* st, size_t scale) const {
+  FreeChunksStatistics totals;
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    st->cr();
+    st->print("%12s chunks: ", chunk_size_name(i));
+    if (_chunk_stats[i].num() > 0) {
+      st->print(UINTX_FORMAT_W(4) ", capacity ", _chunk_stats[i].num());
+      print_scaled_words(st, _chunk_stats[i].cap(), scale);
+    } else {
+      st->print("(none)");
+    }
+    totals.add(_chunk_stats[i]);
+  }
+  st->cr();
+  st->print("%19s: " UINTX_FORMAT_W(4) ", capacity=", "Total", totals.num());
+  print_scaled_words(st, totals.cap(), scale);
+  st->cr();
+}
+
+// UsedChunksStatistics methods
+
+UsedChunksStatistics::UsedChunksStatistics()
+: _num(0), _cap(0), _used(0), _free(0), _waste(0), _overhead(0)
+{}
+
+void UsedChunksStatistics::reset() {
+  _num = 0;
+  _cap = _overhead = _used = _free = _waste = 0;
+}
+
+void UsedChunksStatistics::add(const UsedChunksStatistics& other) {
+  _num += other._num;
+  _cap += other._cap;
+  _used += other._used;
+  _free += other._free;
+  _waste += other._waste;
+  _overhead += other._overhead;
+  DEBUG_ONLY(check_sanity());
+}
+
+void UsedChunksStatistics::print_on(outputStream* st, size_t scale) const {
+  int col = st->position();
+  st->print(UINTX_FORMAT_W(4) " chunk%s, ", _num, _num != 1 ? "s" : "");
+  if (_num > 0) {
+    col += 14; st->fill_to(col);
+
+    print_scaled_words(st, _cap, scale, 5);
+    st->print(" capacity, ");
+
+    col += 18; st->fill_to(col);
+    print_scaled_words_and_percentage(st, _used, _cap, scale, 5);
+    st->print(" used, ");
+
+    col += 20; st->fill_to(col);
+    print_scaled_words_and_percentage(st, _free, _cap, scale, 5);
+    st->print(" free, ");
+
+    col += 20; st->fill_to(col);
+    print_scaled_words_and_percentage(st, _waste, _cap, scale, 5);
+    st->print(" waste, ");
+
+    col += 20; st->fill_to(col);
+    print_scaled_words_and_percentage(st, _overhead, _cap, scale, 5);
+    st->print(" overhead");
+  }
+  DEBUG_ONLY(check_sanity());
+}
+
+#ifdef ASSERT
+void UsedChunksStatistics::check_sanity() const {
+  assert(_overhead == (Metachunk::overhead() * _num), "Sanity: Overhead.");
+  assert(_cap == _used + _free + _waste + _overhead, "Sanity: Capacity.");
+}
+#endif
+
+// SpaceManagerStatistics methods
+
+SpaceManagerStatistics::SpaceManagerStatistics() { reset(); }
+
+void SpaceManagerStatistics::reset() {
+  for (int i = 0; i < NumberOfInUseLists; i ++) {
+    _chunk_stats[i].reset();
+    _free_blocks_num = 0; _free_blocks_cap_words = 0;
+  }
+}
+
+void SpaceManagerStatistics::add_free_blocks_info(uintx num, size_t cap) {
+  _free_blocks_num += num;
+  _free_blocks_cap_words += cap;
+}
+
+void SpaceManagerStatistics::add(const SpaceManagerStatistics& other) {
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    _chunk_stats[i].add(other._chunk_stats[i]);
+  }
+  _free_blocks_num += other._free_blocks_num;
+  _free_blocks_cap_words += other._free_blocks_cap_words;
+}
+
+// Returns total chunk statistics over all chunk types.
+UsedChunksStatistics SpaceManagerStatistics::totals() const {
+  UsedChunksStatistics stat;
+  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+    stat.add(_chunk_stats[i]);
+  }
+  return stat;
+}
+
+void SpaceManagerStatistics::print_on(outputStream* st, size_t scale,  bool detailed) const {
+  streamIndentor sti(st);
+  if (detailed) {
+    st->cr_indent();
+    st->print("Usage by chunk type:");
+    {
+      streamIndentor sti2(st);
+      for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
+        st->cr_indent();
+        st->print("%15s: ", chunk_size_name(i));
+        if (_chunk_stats[i].num() == 0) {
+          st->print(" (none)");
+        } else {
+          _chunk_stats[i].print_on(st, scale);
+        }
+      }
+
+      st->cr_indent();
+      st->print("%15s: ", "-total-");
+      totals().print_on(st, scale);
+    }
+    if (_free_blocks_num > 0) {
+      st->cr_indent();
+      st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num);
+      print_scaled_words(st, _free_blocks_cap_words, scale);
+    }
+  } else {
+    totals().print_on(st, scale);
+    st->print(", ");
+    st->print("deallocated: " UINTX_FORMAT " blocks with ", _free_blocks_num);
+    print_scaled_words(st, _free_blocks_cap_words, scale);
+  }
+}
+
+// ClassLoaderMetaspaceStatistics methods
+
+ClassLoaderMetaspaceStatistics::ClassLoaderMetaspaceStatistics() { reset(); }
+
+void ClassLoaderMetaspaceStatistics::reset() {
+  nonclass_sm_stats().reset();
+  if (Metaspace::using_class_space()) {
+    class_sm_stats().reset();
+  }
+}
+
+// Returns total space manager statistics for both class and non-class metaspace
+SpaceManagerStatistics ClassLoaderMetaspaceStatistics::totals() const {
+  SpaceManagerStatistics stats;
+  stats.add(nonclass_sm_stats());
+  if (Metaspace::using_class_space()) {
+    stats.add(class_sm_stats());
+  }
+  return stats;
+}
+
+void ClassLoaderMetaspaceStatistics::add(const ClassLoaderMetaspaceStatistics& other) {
+  nonclass_sm_stats().add(other.nonclass_sm_stats());
+  if (Metaspace::using_class_space()) {
+    class_sm_stats().add(other.class_sm_stats());
+  }
+}
+
+void ClassLoaderMetaspaceStatistics::print_on(outputStream* st, size_t scale, bool detailed) const {
+  streamIndentor sti(st);
+  st->cr_indent();
+  if (Metaspace::using_class_space()) {
+    st->print("Non-Class: ");
+  }
+  nonclass_sm_stats().print_on(st, scale, detailed);
+  if (detailed) {
+    st->cr();
+  }
+  if (Metaspace::using_class_space()) {
+    st->cr_indent();
+    st->print("    Class: ");
+    class_sm_stats().print_on(st, scale, detailed);
+    if (detailed) {
+      st->cr();
+    }
+    st->cr_indent();
+    st->print("     Both: ");
+    totals().print_on(st, scale, detailed);
+    if (detailed) {
+      st->cr();
+    }
+  }
+  st->cr();
+}
+
+
+} // end namespace internals
+} // end namespace metaspace
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, SAP 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.
+ *
+ */
+
+#ifndef SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_
+#define SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_
+
+#include "utilities/globalDefinitions.hpp"
+#include "memory/metachunk.hpp" // for ChunkIndex enum
+#include "memory/metaspace.hpp" // for MetadataType enum
+
+class outputStream;
+
+namespace metaspace {
+namespace internals {
+
+// Contains statistics for a number of free chunks.
+class FreeChunksStatistics {
+  uintx _num;         // Number of chunks
+  size_t _cap;        // Total capacity, in words
+
+public:
+  FreeChunksStatistics();
+
+  void reset();
+
+  uintx num() const { return _num; }
+  size_t cap() const { return _cap; }
+
+  void add(uintx n, size_t s);
+  void add(const FreeChunksStatistics& other);
+  void print_on(outputStream* st, size_t scale) const;
+
+}; // end: FreeChunksStatistics
+
+
+// Contains statistics for a ChunkManager.
+class ChunkManagerStatistics {
+
+  FreeChunksStatistics _chunk_stats[NumberOfInUseLists];
+
+public:
+
+  // Free chunk statistics, by chunk index.
+  const FreeChunksStatistics& chunk_stats(ChunkIndex index) const   { return _chunk_stats[index]; }
+  FreeChunksStatistics& chunk_stats(ChunkIndex index)               { return _chunk_stats[index]; }
+
+  void reset();
+  size_t total_capacity() const;
+
+  void print_on(outputStream* st, size_t scale) const;
+
+}; // ChunkManagerStatistics
+
+// Contains statistics for a number of chunks in use.
+// Each chunk has a used and free portion; however, there are current chunks (serving
+// potential future metaspace allocations) and non-current chunks. Unused portion of the
+// former is counted as free, unused portion of the latter counts as waste.
+class UsedChunksStatistics {
+  uintx _num;     // Number of chunks
+  size_t _cap;    // Total capacity in words.
+  size_t _used;   // Total used area, in words
+  size_t _free;   // Total free area (unused portions of current chunks), in words
+  size_t _waste;  // Total waste area (unused portions of non-current chunks), in words
+  size_t _overhead; // Total sum of chunk overheads, in words.
+
+public:
+
+  UsedChunksStatistics();
+
+  void reset();
+
+  uintx num() const { return _num; }
+
+  // Total capacity, in words
+  size_t cap() const { return _cap; }
+
+  // Total used area, in words
+  size_t used() const { return _used; }
+
+  // Total free area (unused portions of current chunks), in words
+  size_t free() const { return _free; }
+
+  // Total waste area (unused portions of non-current chunks), in words
+  size_t waste() const { return _waste; }
+
+  // Total area spent in overhead (chunk headers), in words
+  size_t overhead() const { return _overhead; }
+
+  void add_num(uintx n) { _num += n; }
+  void add_cap(size_t s) { _cap += s; }
+  void add_used(size_t s) { _used += s; }
+  void add_free(size_t s) { _free += s; }
+  void add_waste(size_t s) { _waste += s; }
+  void add_overhead(size_t s) { _overhead += s; }
+
+  void add(const UsedChunksStatistics& other);
+
+  void print_on(outputStream* st, size_t scale) const;
+
+#ifdef ASSERT
+  void check_sanity() const;
+#endif
+
+}; // UsedChunksStatistics
+
+// Class containing statistics for one or more space managers.
+class SpaceManagerStatistics {
+
+  UsedChunksStatistics _chunk_stats[NumberOfInUseLists];
+  uintx _free_blocks_num;
+  size_t _free_blocks_cap_words;
+
+public:
+
+  SpaceManagerStatistics();
+
+  // Chunk statistics by chunk index
+  const UsedChunksStatistics& chunk_stats(ChunkIndex index) const   { return _chunk_stats[index]; }
+  UsedChunksStatistics& chunk_stats(ChunkIndex index)               { return _chunk_stats[index]; }
+
+  uintx free_blocks_num () const                                    { return _free_blocks_num; }
+  size_t free_blocks_cap_words () const                             { return _free_blocks_cap_words; }
+
+  void reset();
+
+  void add_free_blocks_info(uintx num, size_t cap);
+
+  // Returns total chunk statistics over all chunk types.
+  UsedChunksStatistics totals() const;
+
+  void add(const SpaceManagerStatistics& other);
+
+  void print_on(outputStream* st, size_t scale,  bool detailed) const;
+
+}; // SpaceManagerStatistics
+
+class ClassLoaderMetaspaceStatistics {
+
+  SpaceManagerStatistics _sm_stats[Metaspace::MetadataTypeCount];
+
+public:
+
+  ClassLoaderMetaspaceStatistics();
+
+  const SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType) const { return _sm_stats[mdType]; }
+  SpaceManagerStatistics& sm_stats(Metaspace::MetadataType mdType)             { return _sm_stats[mdType]; }
+
+  const SpaceManagerStatistics& nonclass_sm_stats() const { return sm_stats(Metaspace::NonClassType); }
+  SpaceManagerStatistics& nonclass_sm_stats()             { return sm_stats(Metaspace::NonClassType); }
+  const SpaceManagerStatistics& class_sm_stats() const    { return sm_stats(Metaspace::ClassType); }
+  SpaceManagerStatistics& class_sm_stats()                { return sm_stats(Metaspace::ClassType); }
+
+  void reset();
+
+  void add(const ClassLoaderMetaspaceStatistics& other);
+
+  // Returns total space manager statistics for both class and non-class metaspace
+  SpaceManagerStatistics totals() const;
+
+  void print_on(outputStream* st, size_t scale, bool detailed) const;
+
+}; // ClassLoaderMetaspaceStatistics
+
+} // namespace internals
+} // namespace metaspace
+
+#endif /* SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP_ */
--- a/src/hotspot/share/runtime/vm_operations.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/runtime/vm_operations.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -235,7 +235,7 @@
 }
 
 void VM_PrintMetadata::doit() {
-  MetaspaceUtils::print_metadata_for_nmt(_out, _scale);
+  MetaspaceUtils::print_report(_out, _scale, _flags);
 }
 
 VM_FindDeadlocks::~VM_FindDeadlocks() {
--- a/src/hotspot/share/runtime/vm_operations.hpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/runtime/vm_operations.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -391,10 +391,14 @@
 
 class VM_PrintMetadata : public VM_Operation {
  private:
-  outputStream* _out;
-  size_t        _scale;
+  outputStream* const _out;
+  const size_t        _scale;
+  const int           _flags;
+
  public:
-  VM_PrintMetadata(outputStream* out, size_t scale) : _out(out), _scale(scale) {};
+  VM_PrintMetadata(outputStream* out, size_t scale, int flags)
+    : _out(out), _scale(scale), _flags(flags)
+  {};
 
   VMOp_Type type() const  { return VMOp_PrintMetadata; }
   void doit();
--- a/src/hotspot/share/services/diagnosticCommand.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/diagnosticCommand.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -29,6 +29,7 @@
 #include "compiler/compileBroker.hpp"
 #include "compiler/directivesParser.hpp"
 #include "gc/shared/vmGCOperations.hpp"
+#include "memory/metaspace/metaspaceDCmd.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
@@ -90,7 +91,7 @@
   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));
-  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<MetaspaceDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<metaspace::MetaspaceDCmd>(full_export, true, false));
 #if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JVMTIAgentLoadDCmd>(full_export, true, false));
 #endif // INCLUDE_JVMTI
--- a/src/hotspot/share/services/diagnosticCommand.hpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/diagnosticCommand.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -866,25 +866,4 @@
   virtual void execute(DCmdSource source, TRAPS);
 };
 
-class MetaspaceDCmd : public DCmd {
-public:
-  MetaspaceDCmd(outputStream* output, bool heap);
-  static const char* name() {
-    return "VM.metaspace";
-  }
-  static const char* description() {
-    return "Prints the statistics for the metaspace";
-  }
-  static const char* impact() {
-      return "Medium: Depends on number of classes loaded.";
-  }
-  static const JavaPermission permission() {
-    JavaPermission p = {"java.lang.management.ManagementPermission",
-                        "monitor", NULL};
-    return p;
-  }
-  static int num_arguments() { return 0; }
-  virtual void execute(DCmdSource source, TRAPS);
-};
-
 #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
--- a/src/hotspot/share/services/memReporter.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/memReporter.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -201,7 +201,7 @@
   size_t used = MetaspaceUtils::used_bytes(type);
   size_t free = (MetaspaceUtils::capacity_bytes(type) - used)
               + MetaspaceUtils::free_chunks_total_bytes(type)
-              + MetaspaceUtils::free_bytes(type);
+              + MetaspaceUtils::free_in_vs_bytes(type);
 
   assert(committed >= used + free, "Sanity");
   size_t waste = committed - (used + free);
--- a/src/hotspot/share/services/memTracker.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/memTracker.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -177,10 +177,12 @@
     } else {
       MemDetailReporter rpt(baseline, output);
       rpt.report();
-
+      output->print("Metaspace:");
       // Metadata reporting requires a safepoint, so avoid it if VM is not in good state.
       assert(!VMError::fatal_error_in_progress(), "Do not report metadata in error report");
-      VM_PrintMetadata vmop(output, K);
+      VM_PrintMetadata vmop(output, K,
+          MetaspaceUtils::rf_show_loaders |
+          MetaspaceUtils::rf_break_down_by_spacetype);
       VMThread::execute(&vmop);
     }
   }
--- a/src/hotspot/share/services/metaspaceDCmd.cpp	Thu May 03 17:36:25 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2018, 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.
- *
- */
-#include "precompiled.hpp"
-#include "memory/metaspace.hpp"
-#include "services/diagnosticCommand.hpp"
-
-MetaspaceDCmd::MetaspaceDCmd(outputStream* output, bool heap): DCmd(output, heap) {
-}
-
-void MetaspaceDCmd::execute(DCmdSource source, TRAPS) {
-  const size_t scale = 1 * K;
-  VM_PrintMetadata op(output(), scale);
-  VMThread::execute(&op);
-}
-
--- a/src/hotspot/share/services/nmtCommon.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/nmtCommon.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -23,6 +23,7 @@
  */
 #include "precompiled.hpp"
 #include "services/nmtCommon.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 const char* NMTUtil::_memory_type_names[] = {
   "Java Heap",
@@ -59,14 +60,13 @@
 
 size_t NMTUtil::scale_from_name(const char* scale) {
   assert(scale != NULL, "Null pointer check");
-  if (strncmp(scale, "KB", 2) == 0 ||
-      strncmp(scale, "kb", 2) == 0) {
+  if (strcasecmp(scale, "1") == 0 || strcasecmp(scale, "b") == 0) {
+    return 1;
+  } else if (strcasecmp(scale, "kb") == 0 || strcasecmp(scale, "k") == 0) {
     return K;
-  } else if (strncmp(scale, "MB", 2) == 0 ||
-             strncmp(scale, "mb", 2) == 0) {
+  } else if (strcasecmp(scale, "mb") == 0 || strcasecmp(scale, "m") == 0) {
     return M;
-  } else if (strncmp(scale, "GB", 2) == 0 ||
-             strncmp(scale, "gb", 2) == 0) {
+  } else if (strcasecmp(scale, "gb") == 0 || strcasecmp(scale, "g") == 0) {
     return G;
   } else {
     return 0; // Invalid value
--- a/src/hotspot/share/services/virtualMemoryTracker.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/services/virtualMemoryTracker.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -617,7 +617,7 @@
 
   size_t free_in_bytes = (MetaspaceUtils::capacity_bytes(type) - MetaspaceUtils::used_bytes(type))
                        + MetaspaceUtils::free_chunks_total_bytes(type)
-                       + MetaspaceUtils::free_bytes(type);
+                       + MetaspaceUtils::free_in_vs_bytes(type);
   mss._free_in_bytes[type] = free_in_bytes;
 }
 
--- a/src/hotspot/share/utilities/ostream.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/utilities/ostream.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -206,6 +206,10 @@
   this->write("\n", 1);
 }
 
+void outputStream::cr_indent() {
+  cr(); indent();
+}
+
 void outputStream::stamp() {
   if (! _stamp.is_updated()) {
     _stamp.update(); // start at 0 on first call to stamp()
--- a/src/hotspot/share/utilities/ostream.hpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/utilities/ostream.hpp	Tue Apr 24 18:06:32 2018 +0200
@@ -102,8 +102,10 @@
    void put(char ch);
    void sp(int count = 1);
    void cr();
+   void cr_indent();
    void bol() { if (_position > 0)  cr(); }
 
+
    // Time stamp
    TimeStamp& time_stamp() { return _stamp; }
    void stamp();
@@ -152,7 +154,6 @@
   ~streamIndentor() { _str->dec(_amount); }
 };
 
-
 // advisory locking for the shared tty stream:
 class ttyLocker: StackObj {
   friend class ttyUnlocker;
--- a/src/hotspot/share/utilities/vmError.cpp	Thu May 03 17:36:25 2018 -0700
+++ b/src/hotspot/share/utilities/vmError.cpp	Tue Apr 24 18:06:32 2018 +0200
@@ -862,6 +862,13 @@
        st->cr();
      }
 
+  STEP("printing metaspace information")
+
+     if (_verbose && Universe::is_fully_initialized()) {
+       st->print_cr("Metaspace:");
+       MetaspaceUtils::print_basic_report(st, 0);
+     }
+
   STEP("printing code cache information")
 
      if (_verbose && Universe::is_fully_initialized()) {
@@ -1046,6 +1053,13 @@
     st->cr();
   }
 
+  // STEP("printing metaspace information")
+
+  if (Universe::is_fully_initialized()) {
+    st->print_cr("Metaspace:");
+    MetaspaceUtils::print_basic_report(st, 0);
+  }
+
   // STEP("printing code cache information")
 
   if (Universe::is_fully_initialized()) {
--- a/test/hotspot/jtreg/TEST.ROOT	Thu May 03 17:36:25 2018 -0700
+++ b/test/hotspot/jtreg/TEST.ROOT	Tue Apr 24 18:06:32 2018 +0200
@@ -27,7 +27,7 @@
 # It also contains test-suite configuration information.
 
 # The list of keywords supported in this test suite
-keys=cte_test jcmd nmt regression gc stress
+keys=cte_test jcmd nmt regression gc stress metaspace
 
 groups=TEST.groups
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java	Tue Apr 24 18:06:32 2018 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, SAP 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 jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+
+/*
+ * @test
+ * @key metaspace jcmd
+ * @summary Test the VM.metaspace command
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:+UseCompressedClassPointers PrintMetaspaceDcmd with-compressed-class-space
+ * @run main/othervm -XX:MaxMetaspaceSize=201M -XX:+VerifyMetaspace -XX:-UseCompressedClassPointers PrintMetaspaceDcmd without-compressed-class-space
+ */
+
+public class PrintMetaspaceDcmd {
+
+    // Run jcmd VM.metaspace against a VM with CompressedClassPointers on.
+    // The report should detail Non-Class and Class portions separately.
+    private static void doTheTest(boolean usesCompressedClassSpace) throws Exception {
+        ProcessBuilder pb = new ProcessBuilder();
+        OutputAnalyzer output;
+        // Grab my own PID
+        String pid = Long.toString(ProcessTools.getProcessId());
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "basic"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        if (usesCompressedClassSpace) {
+            output.shouldContain("Non-Class:");
+            output.shouldContain("Class:");
+        }
+        output.shouldContain("Virtual space:");
+        output.shouldContain("Chunk freelists:");
+
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        if (usesCompressedClassSpace) {
+            output.shouldContain("Non-Class:");
+            output.shouldContain("Class:");
+        }
+        output.shouldContain("Virtual space:");
+        output.shouldContain("Chunk freelist");
+        output.shouldContain("Waste");
+        output.shouldMatch("MaxMetaspaceSize:.*201.00.*MB");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "show-loaders"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("ClassLoaderData.*for <bootloader>");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "by-chunktype"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldContain("specialized:");
+        output.shouldContain("small:");
+        output.shouldContain("medium:");
+        output.shouldContain("humongous:");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vslist"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldContain("Virtual space list");
+        output.shouldMatch("node.*reserved.*committed.*used.*");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "vsmap"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldContain("Virtual space map:");
+        output.shouldContain("HHHHHHHHHHH");
+
+        // Test with different scales
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=G"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("MaxMetaspaceSize:.*0.2.*GB");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=K"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("MaxMetaspaceSize:.*205824.00 KB");
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=1"});
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("MaxMetaspaceSize:.*210763776 bytes");
+    }
+
+    public static void main(String args[]) throws Exception {
+        boolean testForCompressedClassSpace = false;
+        if (args[0].equals("with-compressed-class-space")) {
+            testForCompressedClassSpace = true;
+        } else if (args[0].equals("without-compressed-class-space")) {
+            testForCompressedClassSpace = false;
+        } else {
+            throw new IllegalArgumentException("Invalid argument: " + args[0]);
+        }
+        doTheTest(testForCompressedClassSpace);
+    }
+}
--- a/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java	Thu May 03 17:36:25 2018 -0700
+++ b/test/hotspot/jtreg/runtime/NMT/PrintNMTStatistics.java	Tue Apr 24 18:06:32 2018 +0200
@@ -46,6 +46,10 @@
     OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
     output_detail.shouldContain("Virtual memory map:");
     output_detail.shouldContain("Details:");
+
+    // PrintNMTStatistics also prints out metaspace statistics as a convenience.
+    output_detail.shouldContain("Metaspace:");
+
     output_detail.shouldHaveExitValue(0);
 
     // Make sure memory reserved for Module processing is recorded.