8218988: Improve metaspace verifications
authorstuefe
Sun, 10 Feb 2019 09:10:42 +0100
changeset 53970 1ad7c590a6e7
parent 53969 b342deab639f
child 53971 1019c97e1bde
8218988: Improve metaspace verifications Reviewed-by: zgu, coleenp
src/hotspot/share/memory/metaspace.cpp
src/hotspot/share/memory/metaspace/chunkManager.cpp
src/hotspot/share/memory/metaspace/chunkManager.hpp
src/hotspot/share/memory/metaspace/metaDebug.hpp
src/hotspot/share/memory/metaspace/metaspaceCommon.hpp
src/hotspot/share/memory/metaspace/spaceManager.cpp
src/hotspot/share/memory/metaspace/virtualSpaceList.cpp
src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp
src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp
src/hotspot/share/runtime/globals.hpp
--- a/src/hotspot/share/memory/metaspace.cpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace.cpp	Sun Feb 10 09:10:42 2019 +0100
@@ -440,7 +440,6 @@
   if (chunk_manager == NULL) {
     return 0;
   }
-  chunk_manager->slow_verify();
   return chunk_manager->free_chunks_total_words();
 }
 
@@ -793,6 +792,13 @@
   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->print_cr("Number of chunks added to freelist: " UINTX_FORMAT ".",
+                g_internal_statistics.num_chunks_added_to_freelist);
+  out->print_cr("Number of chunks removed from freelist: " UINTX_FORMAT ".",
+                g_internal_statistics.num_chunks_removed_from_freelist);
+  out->print_cr("Number of chunk merges: " UINTX_FORMAT ", split-ups: " UINTX_FORMAT ".",
+                g_internal_statistics.num_chunk_merges, g_internal_statistics.num_chunk_splits);
+
   out->cr();
 #endif
 
@@ -844,10 +850,12 @@
 }
 
 void MetaspaceUtils::verify_free_chunks() {
-  Metaspace::chunk_manager_metadata()->verify();
+#ifdef ASSERT
+  Metaspace::chunk_manager_metadata()->verify(false);
   if (Metaspace::using_class_space()) {
-    Metaspace::chunk_manager_class()->verify();
+    Metaspace::chunk_manager_class()->verify(false);
   }
+#endif
 }
 
 void MetaspaceUtils::verify_metrics() {
--- a/src/hotspot/share/memory/metaspace/chunkManager.cpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/chunkManager.cpp	Sun Feb 10 09:10:42 2019 +0100
@@ -29,6 +29,7 @@
 #include "memory/freeList.inline.hpp"
 #include "memory/metaspace/chunkManager.hpp"
 #include "memory/metaspace/metachunk.hpp"
+#include "memory/metaspace/metaDebug.hpp"
 #include "memory/metaspace/metaspaceCommon.hpp"
 #include "memory/metaspace/metaspaceStatistics.hpp"
 #include "memory/metaspace/occupancyMap.hpp"
@@ -140,15 +141,21 @@
   _free_chunks_count -= num_chunks_removed;
   _free_chunks_count ++;
 
-  // VirtualSpaceNode::container_count does not have to be modified:
+  // VirtualSpaceNode::chunk_count does not have to be modified:
   // it means "number of active (non-free) chunks", so merging free chunks
   // should not affect that count.
 
   // At the end of a chunk merge, run verification tests.
-  if (VerifyMetaspace) {
-    DEBUG_ONLY(this->locked_verify());
-    DEBUG_ONLY(vsn->verify());
-  }
+#ifdef ASSERT
+
+  EVERY_NTH(VerifyMetaspaceInterval)
+    locked_verify(true);
+    vsn->verify(true);
+  END_EVERY_NTH
+
+  g_internal_statistics.num_chunk_merges ++;
+
+#endif
 
   return true;
 }
@@ -189,14 +196,6 @@
   return num_removed;
 }
 
-size_t ChunkManager::free_chunks_total_words() {
-  return _free_chunks_total;
-}
-
-size_t ChunkManager::free_chunks_total_bytes() {
-  return free_chunks_total_words() * BytesPerWord;
-}
-
 // Update internal accounting after a chunk was added
 void ChunkManager::account_for_added_chunk(const Metachunk* c) {
   assert_lock_strong(MetaspaceExpand_lock);
@@ -216,19 +215,6 @@
   _free_chunks_total -= c->word_size();
 }
 
-size_t ChunkManager::free_chunks_count() {
-#ifdef ASSERT
-  if (!UseConcMarkSweepGC && !MetaspaceExpand_lock->is_locked()) {
-    MutexLockerEx cl(MetaspaceExpand_lock,
-                     Mutex::_no_safepoint_check_flag);
-    // This lock is only needed in debug because the verification
-    // of the _free_chunks_totals walks the list of free chunks
-    slow_locked_verify_free_chunks_count();
-  }
-#endif
-  return _free_chunks_count;
-}
-
 ChunkIndex ChunkManager::list_index(size_t size) {
   return get_chunk_type_by_size(size, is_class());
 }
@@ -239,43 +225,48 @@
   return get_size_for_nonhumongous_chunktype(index, is_class());
 }
 
-void ChunkManager::locked_verify_free_chunks_total() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  assert(sum_free_chunks() == _free_chunks_total,
-         "_free_chunks_total " SIZE_FORMAT " is not the"
-         " same as sum " SIZE_FORMAT, _free_chunks_total,
-         sum_free_chunks());
+#ifdef ASSERT
+void ChunkManager::verify(bool slow) const {
+  MutexLockerEx cl(MetaspaceExpand_lock,
+                     Mutex::_no_safepoint_check_flag);
+  locked_verify(slow);
 }
 
-void ChunkManager::locked_verify_free_chunks_count() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  assert(sum_free_chunks_count() == _free_chunks_count,
-         "_free_chunks_count " SIZE_FORMAT " is not the"
-         " same as sum " SIZE_FORMAT, _free_chunks_count,
-         sum_free_chunks_count());
-}
+void ChunkManager::locked_verify(bool slow) const {
+  log_trace(gc, metaspace, freelist)("verifying %s chunkmanager (%s).",
+    (is_class() ? "class space" : "metaspace"), (slow ? "slow" : "quick"));
 
-void ChunkManager::verify() {
-  MutexLockerEx cl(MetaspaceExpand_lock,
-                     Mutex::_no_safepoint_check_flag);
-  locked_verify();
-}
+  assert_lock_strong(MetaspaceExpand_lock);
 
-void ChunkManager::locked_verify() {
-  locked_verify_free_chunks_count();
-  locked_verify_free_chunks_total();
+  size_t chunks_counted = 0;
+  size_t wordsize_chunks_counted = 0;
   for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
-    ChunkList* list = free_chunks(i);
+    const ChunkList* list = _free_chunks + i;
     if (list != NULL) {
       Metachunk* chunk = list->head();
       while (chunk) {
-        DEBUG_ONLY(do_verify_chunk(chunk);)
+        if (slow) {
+          do_verify_chunk(chunk);
+        }
         assert(chunk->is_tagged_free(), "Chunk should be tagged as free.");
+        chunks_counted ++;
+        wordsize_chunks_counted += chunk->size();
         chunk = chunk->next();
       }
     }
   }
+
+  chunks_counted += humongous_dictionary()->total_free_blocks();
+  wordsize_chunks_counted += humongous_dictionary()->total_size();
+
+  assert(chunks_counted == _free_chunks_count && wordsize_chunks_counted == _free_chunks_total,
+         "freelist accounting mismatch: "
+         "we think: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words, "
+         "reality: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words.",
+         _free_chunks_count, _free_chunks_total,
+         chunks_counted, wordsize_chunks_counted);
 }
+#endif // ASSERT
 
 void ChunkManager::locked_print_free_chunks(outputStream* st) {
   assert_lock_strong(MetaspaceExpand_lock);
@@ -283,51 +274,12 @@
                 _free_chunks_total, _free_chunks_count);
 }
 
-void ChunkManager::locked_print_sum_free_chunks(outputStream* st) {
-  assert_lock_strong(MetaspaceExpand_lock);
-  st->print_cr("Sum free chunk total " SIZE_FORMAT "  count " SIZE_FORMAT,
-                sum_free_chunks(), sum_free_chunks_count());
-}
-
 ChunkList* ChunkManager::free_chunks(ChunkIndex index) {
   assert(index == SpecializedIndex || index == SmallIndex || index == MediumIndex,
          "Bad index: %d", (int)index);
-
   return &_free_chunks[index];
 }
 
-// These methods that sum the free chunk lists are used in printing
-// methods that are used in product builds.
-size_t ChunkManager::sum_free_chunks() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  size_t result = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
-    ChunkList* list = free_chunks(i);
-
-    if (list == NULL) {
-      continue;
-    }
-
-    result = result + list->count() * list->size();
-  }
-  result = result + humongous_dictionary()->total_size();
-  return result;
-}
-
-size_t ChunkManager::sum_free_chunks_count() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  size_t count = 0;
-  for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) {
-    ChunkList* list = free_chunks(i);
-    if (list == NULL) {
-      continue;
-    }
-    count = count + list->count();
-  }
-  count = count + humongous_dictionary()->total_free_blocks();
-  return count;
-}
-
 ChunkList* ChunkManager::find_free_chunks_list(size_t word_size) {
   ChunkIndex index = list_index(word_size);
   assert(index < HumongousIndex, "No humongous list");
@@ -427,14 +379,18 @@
 
   }
 
+  // Note: at this point, the VirtualSpaceNode is invalid since we split a chunk and
+  // did not yet hand out part of that split; so, vsn->verify_free_chunks_are_ideally_merged()
+  // would assert. Instead, do all verifications in the caller.
+
+  DEBUG_ONLY(g_internal_statistics.num_chunk_splits ++);
+
   return target_chunk;
 }
 
 Metachunk* ChunkManager::free_chunks_get(size_t word_size) {
   assert_lock_strong(MetaspaceExpand_lock);
 
-  slow_locked_verify();
-
   Metachunk* chunk = NULL;
   bool we_did_split_a_chunk = false;
 
@@ -524,14 +480,19 @@
 
   // Run some verifications (some more if we did a chunk split)
 #ifdef ASSERT
-  if (VerifyMetaspace) {
-    locked_verify();
+
+  EVERY_NTH(VerifyMetaspaceInterval)
+    // Be extra verify-y when chunk split happened.
+    locked_verify(true);
     VirtualSpaceNode* const vsn = chunk->container();
-    vsn->verify();
+    vsn->verify(true);
     if (we_did_split_a_chunk) {
       vsn->verify_free_chunks_are_ideally_merged();
     }
-  }
+  END_EVERY_NTH
+
+  g_internal_statistics.num_chunks_removed_from_freelist ++;
+
 #endif
 
   return chunk;
@@ -539,7 +500,6 @@
 
 Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) {
   assert_lock_strong(MetaspaceExpand_lock);
-  slow_locked_verify();
 
   // Take from the beginning of the list
   Metachunk* chunk = free_chunks_get(word_size);
@@ -570,9 +530,17 @@
 }
 
 void ChunkManager::return_single_chunk(Metachunk* chunk) {
+
+#ifdef ASSERT
+  EVERY_NTH(VerifyMetaspaceInterval)
+    this->locked_verify(false);
+    do_verify_chunk(chunk);
+  END_EVERY_NTH
+#endif
+
   const ChunkIndex index = chunk->get_chunk_type();
   assert_lock_strong(MetaspaceExpand_lock);
-  DEBUG_ONLY(do_verify_chunk(chunk);)
+  DEBUG_ONLY(g_internal_statistics.num_chunks_added_to_freelist ++;)
   assert(chunk != NULL, "Expected chunk.");
   assert(chunk->container() != NULL, "Container should have been set.");
   assert(chunk->is_tagged_free() == false, "Chunk should be in use.");
@@ -583,6 +551,9 @@
   // keeps tree node pointers in the chunk payload area which mangle will overwrite.
   DEBUG_ONLY(chunk->mangle(badMetaWordVal);)
 
+  // may need node for verification later after chunk may have been merged away.
+  DEBUG_ONLY(VirtualSpaceNode* vsn = chunk->container(); )
+
   if (index != HumongousIndex) {
     // Return non-humongous chunk to freelist.
     ChunkList* list = free_chunks(index);
@@ -618,6 +589,16 @@
     }
   }
 
+  // From here on do not access chunk anymore, it may have been merged with another chunk.
+
+#ifdef ASSERT
+  EVERY_NTH(VerifyMetaspaceInterval)
+    this->locked_verify(true);
+    vsn->verify(true);
+    vsn->verify_free_chunks_are_ideally_merged();
+  END_EVERY_NTH
+#endif
+
 }
 
 void ChunkManager::return_chunk_list(Metachunk* chunks) {
--- a/src/hotspot/share/memory/metaspace/chunkManager.hpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/chunkManager.hpp	Sun Feb 10 09:10:42 2019 +0100
@@ -33,7 +33,7 @@
 #include "memory/metaspaceChunkFreeListSummary.hpp"
 #include "utilities/globalDefinitions.hpp"
 
-class ChunkManagerTest;
+class ChunkManagerTestAccessor;
 
 namespace metaspace {
 
@@ -42,7 +42,7 @@
 
 // Manages the global free lists of chunks.
 class ChunkManager : public CHeapObj<mtInternal> {
-  friend class ::ChunkManagerTest;
+  friend class ::ChunkManagerTestAccessor;
 
   // Free list of chunks of different sizes.
   //   SpecializedChunk
@@ -63,9 +63,8 @@
   ChunkTreeDictionary _humongous_dictionary;
 
   // Returns the humongous chunk dictionary.
-  ChunkTreeDictionary* humongous_dictionary() {
-    return &_humongous_dictionary;
-  }
+  ChunkTreeDictionary* humongous_dictionary() { return &_humongous_dictionary; }
+  const ChunkTreeDictionary* humongous_dictionary() const { return &_humongous_dictionary; }
 
   // Size, in metaspace words, of all chunks managed by this ChunkManager
   size_t _free_chunks_total;
@@ -76,22 +75,6 @@
   void account_for_added_chunk(const Metachunk* c);
   void account_for_removed_chunk(const Metachunk* c);
 
-  size_t sum_free_chunks();
-  size_t sum_free_chunks_count();
-
-  void locked_verify_free_chunks_total();
-  void slow_locked_verify_free_chunks_total() {
-    if (VerifyMetaspace) {
-      locked_verify_free_chunks_total();
-    }
-  }
-  void locked_verify_free_chunks_count();
-  void slow_locked_verify_free_chunks_count() {
-    if (VerifyMetaspace) {
-      locked_verify_free_chunks_count();
-    }
-  }
-
   // Given a pointer to a chunk, attempts to merge it with neighboring
   // free chunks to form a bigger chunk. Returns true if successful.
   bool attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type);
@@ -147,11 +130,11 @@
   void return_chunk_list(Metachunk* chunk);
 
   // Total of the space in the free chunks list
-  size_t free_chunks_total_words();
-  size_t free_chunks_total_bytes();
+  size_t free_chunks_total_words() const { return _free_chunks_total; }
+  size_t free_chunks_total_bytes() const { return free_chunks_total_words() * BytesPerWord; }
 
   // Number of chunks in the free chunks list
-  size_t free_chunks_count();
+  size_t free_chunks_count() const { return _free_chunks_count; }
 
   // Remove from a list by size.  Selects list based on size of chunk.
   Metachunk* free_chunks_get(size_t chunk_word_size);
@@ -195,22 +178,14 @@
                                          size_free_chunks_in_bytes(HumongousIndex));
   }
 
+#ifdef ASSERT
   // Debug support
-  void verify();
-  void slow_verify() {
-    if (VerifyMetaspace) {
-      verify();
-    }
-  }
-  void locked_verify();
-  void slow_locked_verify() {
-    if (VerifyMetaspace) {
-      locked_verify();
-    }
-  }
+  // Verify free list integrity. slow=true: verify chunk-internal integrity too.
+  void verify(bool slow) const;
+  void locked_verify(bool slow) const;
+#endif
 
   void locked_print_free_chunks(outputStream* st);
-  void locked_print_sum_free_chunks(outputStream* st);
 
   // Fill in current statistic values to the given statistics object.
   void collect_statistics(ChunkManagerStatistics* out) const;
--- a/src/hotspot/share/memory/metaspace/metaDebug.hpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/metaDebug.hpp	Sun Feb 10 09:10:42 2019 +0100
@@ -41,6 +41,17 @@
 #endif
 };
 
+#ifdef ASSERT
+#define EVERY_NTH(n)          \
+{ static int counter_ = 0;    \
+  if (n > 0) {                \
+    counter_ ++;              \
+    if (counter_ > n) {       \
+      counter_ = 0;           \
+
+#define END_EVERY_NTH         } } }
+#endif // ASSERT
+
 } // namespace metaspace
 
 #endif // SHARE_MEMORY_METASPACE_METADEBUG_HPP
--- a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp	Sun Feb 10 09:10:42 2019 +0100
@@ -25,6 +25,7 @@
 #ifndef SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP
 #define SHARE_MEMORY_METASPACE_METASPACECOMMON_HPP
 
+#include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
 
@@ -85,6 +86,14 @@
   uintx num_external_deallocs;
   // Number of times an allocation was satisfied from deallocated blocks.
   uintx num_allocs_from_deallocated_blocks;
+  // Number of times a chunk was added to the freelist
+  uintx num_chunks_added_to_freelist;
+  // Number of times a chunk was removed from the freelist
+  uintx num_chunks_removed_from_freelist;
+  // Number of chunk merges
+  uintx num_chunk_merges;
+  // Number of chunk splits
+  uintx num_chunk_splits;
 };
 extern internal_statistics_t g_internal_statistics;
 #endif
@@ -111,6 +120,16 @@
 // Returns a descriptive name for a chunk type.
 const char* chunk_size_name(ChunkIndex index);
 
+// Verify chunk sizes.
+inline bool is_valid_chunksize(bool is_class, size_t size) {
+  const size_t reasonable_maximum_humongous_chunk_size = 1 * G;
+  return is_aligned(size, sizeof(MetaWord)) &&
+         size < reasonable_maximum_humongous_chunk_size &&
+         is_class ?
+             (size == ClassSpecializedChunk || size == ClassSmallChunk || size >= ClassMediumChunk) :
+             (size == SpecializedChunk || size == SmallChunk || size >= MediumChunk);
+}
+
 // Verify chunk type.
 inline bool is_valid_chunktype(ChunkIndex index) {
   return index == SpecializedIndex || index == SmallIndex ||
--- a/src/hotspot/share/memory/metaspace/spaceManager.cpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/spaceManager.cpp	Sun Feb 10 09:10:42 2019 +0100
@@ -287,8 +287,6 @@
   MutexLockerEx fcl(MetaspaceExpand_lock,
                     Mutex::_no_safepoint_check_flag);
 
-  chunk_manager()->slow_locked_verify();
-
   account_for_spacemanager_death();
 
   Log(gc, metaspace, freelist) log;
@@ -313,7 +311,11 @@
   _current_chunk = NULL;
 #endif
 
-  chunk_manager()->slow_locked_verify();
+#ifdef ASSERT
+  EVERY_NTH(VerifyMetaspaceInterval)
+    chunk_manager()->locked_verify(true);
+  END_EVERY_NTH
+#endif
 
   if (_block_freelists != NULL) {
     delete _block_freelists;
@@ -405,8 +407,6 @@
   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
@@ -422,6 +422,12 @@
     p = allocate_work(raw_word_size);
   }
 
+#ifdef ASSERT
+  EVERY_NTH(VerifyMetaspaceInterval)
+    verify_metrics_locked();
+  END_EVERY_NTH
+#endif
+
   return p;
 }
 
--- a/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/virtualSpaceList.cpp	Sun Feb 10 09:10:42 2019 +0100
@@ -97,7 +97,7 @@
   VirtualSpaceNode* next_vsl = prev_vsl;
   while (next_vsl != NULL) {
     VirtualSpaceNode* vsl = next_vsl;
-    DEBUG_ONLY(vsl->verify_container_count();)
+    DEBUG_ONLY(vsl->verify(false);)
     next_vsl = vsl->next();
     // Don't free the current virtual space since it will likely
     // be needed soon.
--- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp	Sun Feb 10 09:10:42 2019 +0100
@@ -29,6 +29,7 @@
 #include "memory/metaspace/metachunk.hpp"
 #include "memory/metaspace.hpp"
 #include "memory/metaspace/chunkManager.hpp"
+#include "memory/metaspace/metaDebug.hpp"
 #include "memory/metaspace/metaspaceCommon.hpp"
 #include "memory/metaspace/occupancyMap.hpp"
 #include "memory/metaspace/virtualSpaceNode.hpp"
@@ -73,8 +74,9 @@
 }
 
 void VirtualSpaceNode::purge(ChunkManager* chunk_manager) {
-  DEBUG_ONLY(this->verify();)
-    Metachunk* chunk = first_chunk();
+  // When a node is purged, lets give it a thorough examination.
+  DEBUG_ONLY(verify(true);)
+  Metachunk* chunk = first_chunk();
   Metachunk* invalid_chunk = (Metachunk*) top();
   while (chunk < invalid_chunk ) {
     assert(chunk->is_tagged_free(), "Should be tagged free");
@@ -171,49 +173,39 @@
 
 
 #ifdef ASSERT
-uintx VirtualSpaceNode::container_count_slow() {
-  uintx count = 0;
-  Metachunk* chunk = first_chunk();
-  Metachunk* invalid_chunk = (Metachunk*) top();
-  while (chunk < invalid_chunk ) {
-    MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
-    do_verify_chunk(chunk);
-    // Don't count the chunks on the free lists.  Those are
-    // still part of the VirtualSpaceNode but not currently
-    // counted.
-    if (!chunk->is_tagged_free()) {
-      count++;
-    }
-    chunk = (Metachunk*) next;
-  }
-  return count;
-}
-#endif
 
-#ifdef ASSERT
 // Verify counters, all chunks in this list node and the occupancy map.
-void VirtualSpaceNode::verify() {
+void VirtualSpaceNode::verify(bool slow) {
+  log_trace(gc, metaspace, freelist)("verifying %s virtual space node (%s).",
+    (is_class() ? "class space" : "metaspace"), (slow ? "slow" : "quick"));
+  // Fast mode: just verify chunk counters and basic geometry
+  // Slow mode: verify chunks and occupancy map
   uintx num_in_use_chunks = 0;
   Metachunk* chunk = first_chunk();
   Metachunk* invalid_chunk = (Metachunk*) top();
 
   // Iterate the chunks in this node and verify each chunk.
   while (chunk < invalid_chunk ) {
-    DEBUG_ONLY(do_verify_chunk(chunk);)
-      if (!chunk->is_tagged_free()) {
-        num_in_use_chunks ++;
-      }
-    MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
+    if (slow) {
+      do_verify_chunk(chunk);
+    }
+    if (!chunk->is_tagged_free()) {
+      num_in_use_chunks ++;
+    }
+    const size_t s = chunk->word_size();
+    // Prevent endless loop on invalid chunk size.
+    assert(is_valid_chunksize(is_class(), s), "Invalid chunk size: " SIZE_FORMAT ".", s);
+    MetaWord* next = ((MetaWord*)chunk) + s;
     chunk = (Metachunk*) next;
   }
   assert(_container_count == num_in_use_chunks, "Container count mismatch (real: " UINTX_FORMAT
       ", counter: " UINTX_FORMAT ".", num_in_use_chunks, _container_count);
   // Also verify the occupancy map.
-  occupancy_map()->verify(this->bottom(), this->top());
+  if (slow) {
+    occupancy_map()->verify(bottom(), top());
+  }
 }
-#endif // ASSERT
 
-#ifdef ASSERT
 // Verify that all free chunks in this node are ideally merged
 // (there not should be multiple small chunks where a large chunk could exist.)
 void VirtualSpaceNode::verify_free_chunks_are_ideally_merged() {
@@ -224,23 +216,31 @@
   const size_t size_small = (is_class() ? ClassSmallChunk : SmallChunk) * BytesPerWord;
   int num_free_chunks_since_last_med_boundary = -1;
   int num_free_chunks_since_last_small_boundary = -1;
-  while (chunk < invalid_chunk ) {
+  bool error = false;
+  char err[256];
+  while (!error && chunk < invalid_chunk ) {
     // Test for missed chunk merge opportunities: count number of free chunks since last chunk boundary.
     // Reset the counter when encountering a non-free chunk.
     if (chunk->get_chunk_type() != HumongousIndex) {
       if (chunk->is_tagged_free()) {
         // Count successive free, non-humongous chunks.
         if (is_aligned(chunk, size_small)) {
-          assert(num_free_chunks_since_last_small_boundary <= 1,
-              "Missed chunk merge opportunity at " PTR_FORMAT " for chunk size " SIZE_FORMAT_HEX ".", p2i(chunk) - size_small, size_small);
-          num_free_chunks_since_last_small_boundary = 0;
+          if (num_free_chunks_since_last_small_boundary > 0) {
+            error = true;
+            jio_snprintf(err, sizeof(err), "Missed chunk merge opportunity to merge a small chunk preceding " PTR_FORMAT ".", p2i(chunk));
+          } else {
+            num_free_chunks_since_last_small_boundary = 0;
+          }
         } else if (num_free_chunks_since_last_small_boundary != -1) {
           num_free_chunks_since_last_small_boundary ++;
         }
         if (is_aligned(chunk, size_med)) {
-          assert(num_free_chunks_since_last_med_boundary <= 1,
-              "Missed chunk merge opportunity at " PTR_FORMAT " for chunk size " SIZE_FORMAT_HEX ".", p2i(chunk) - size_med, size_med);
-          num_free_chunks_since_last_med_boundary = 0;
+          if (num_free_chunks_since_last_med_boundary > 0) {
+            error = true;
+            jio_snprintf(err, sizeof(err), "Missed chunk merge opportunity to merge a medium chunk preceding " PTR_FORMAT ".", p2i(chunk));
+          } else {
+            num_free_chunks_since_last_med_boundary = 0;
+          }
         } else if (num_free_chunks_since_last_med_boundary != -1) {
           num_free_chunks_since_last_med_boundary ++;
         }
@@ -255,6 +255,11 @@
       num_free_chunks_since_last_small_boundary = -1;
     }
 
+    if (error) {
+      print_map(tty, is_class());
+      fatal("%s", err);
+    }
+
     MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
     chunk = (Metachunk*) next;
   }
@@ -271,14 +276,6 @@
   _container_count--;
 }
 
-#ifdef ASSERT
-void VirtualSpaceNode::verify_container_count() {
-  assert(_container_count == container_count_slow(),
-      "Inconsistency in container_count _container_count " UINTX_FORMAT
-      " container_count_slow() " UINTX_FORMAT, _container_count, container_count_slow());
-}
-#endif
-
 VirtualSpaceNode::~VirtualSpaceNode() {
   _rs.release();
   if (_occupancy_map != NULL) {
@@ -309,7 +306,7 @@
   assert(target_top > top(), "Sanity");
 
   // Padding chunks are added to the freelist.
-  ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(this->is_class());
+  ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(is_class());
 
   // shorthands
   const size_t spec_word_size = chunk_manager->specialized_chunk_word_size();
@@ -378,7 +375,7 @@
   // chunks. These chunks are created and added to the freelist.
 
   // The chunk manager to which we will give our padding chunks.
-  ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(this->is_class());
+  ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(is_class());
 
   // shorthands
   const size_t spec_word_size = chunk_manager->specialized_chunk_word_size();
@@ -450,12 +447,13 @@
 
   inc_container_count();
 
-  if (VerifyMetaspace) {
-    DEBUG_ONLY(chunk_manager->locked_verify());
-    DEBUG_ONLY(this->verify());
-  }
-
-  DEBUG_ONLY(do_verify_chunk(result));
+#ifdef ASSERT
+  EVERY_NTH(VerifyMetaspaceInterval)
+    chunk_manager->locked_verify(true);
+    verify(true);
+  END_EVERY_NTH
+  do_verify_chunk(result);
+#endif
 
   result->inc_use_count();
 
@@ -558,8 +556,13 @@
 #endif // ASSERT
 
 void VirtualSpaceNode::retire(ChunkManager* chunk_manager) {
-  DEBUG_ONLY(verify_container_count();)
-  assert(this->is_class() == chunk_manager->is_class(), "Wrong ChunkManager?");
+  assert(is_class() == chunk_manager->is_class(), "Wrong ChunkManager?");
+#ifdef ASSERT
+  verify(false);
+  EVERY_NTH(VerifyMetaspaceInterval)
+    verify(true);
+  END_EVERY_NTH
+#endif
   for (int i = (int)MediumIndex; i >= (int)ZeroIndex; --i) {
     ChunkIndex index = (ChunkIndex)i;
     size_t chunk_size = chunk_manager->size_by_index(index);
@@ -577,7 +580,6 @@
       }
       chunk_manager->return_single_chunk(chunk);
     }
-    DEBUG_ONLY(verify_container_count();)
   }
   assert(free_words_in_vs() == 0, "should be empty now");
 }
--- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.hpp	Sun Feb 10 09:10:42 2019 +0100
@@ -115,10 +115,6 @@
   uintx container_count() { return _container_count; }
   void inc_container_count();
   void dec_container_count();
-#ifdef ASSERT
-  uintx container_count_slow();
-  void verify_container_count();
-#endif
 
   // used and capacity in this single entry in the list
   size_t used_words_in_vs() const;
@@ -152,10 +148,10 @@
 
   // Debug support
   DEBUG_ONLY(void mangle();)
-  // Verify counters, all chunks in this list node and the occupancy map.
-  DEBUG_ONLY(void verify();)
+  // Verify counters and basic structure. Slow mode: verify all chunks in depth and occupancy map.
+  DEBUG_ONLY(void verify(bool slow);)
   // Verify that all free chunks in this node are ideally merged
-  // (there not should be multiple small chunks where a large chunk could exist.)
+  // (there should not be multiple small chunks where a large chunk could exist.)
   DEBUG_ONLY(void verify_free_chunks_are_ideally_merged();)
 
 };
--- a/src/hotspot/share/runtime/globals.hpp	Fri Mar 01 10:15:04 2019 +0000
+++ b/src/hotspot/share/runtime/globals.hpp	Sun Feb 10 09:10:42 2019 +0100
@@ -2525,8 +2525,9 @@
           "File of size Xmx is pre-allocated for performance reason, so"    \
           "we need that much space available")                              \
                                                                             \
-  develop(bool, VerifyMetaspace, false,                                     \
-          "Verify metaspace on chunk movements.")                           \
+  develop(int, VerifyMetaspaceInterval, DEBUG_ONLY(500) NOT_DEBUG(0),       \
+               "Run periodic metaspace verifications (0 - none, "           \
+               "1 - always, >1 every nth interval)")                        \
                                                                             \
   diagnostic(bool, ShowRegistersOnAssert, true,                             \
           "On internal errors, include registers in error report.")         \