src/hotspot/share/memory/metaspace/chunkManager.cpp
changeset 53970 1ad7c590a6e7
parent 50811 f533eb5e7430
child 54623 1126f0607c70
--- 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) {