src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp
branchstuefe-improved-metaspace
changeset 57464 32e61f51ee09
parent 53970 1ad7c590a6e7
--- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp	Wed Jul 10 05:12:23 2019 +0100
+++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp	Mon Jul 08 22:30:19 2019 +0200
@@ -42,6 +42,35 @@
 
 namespace metaspace {
 
+
+// Create a new empty node of the given size. Memory will be reserved but
+// completely uncommitted.
+VirtualSpaceNode::VirtualSpaceNode(size_t wordsize)
+  : _next(NULL)
+  , _rs()
+  , _virtual_space()
+  , _top(NULL)
+{
+}
+
+// Create a new empty node spanning the given reserved space.
+VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs)
+  : _next(NULL)
+  , _rs(rs)
+  , _virtual_space()
+  , _top(NULL)
+{}
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
 // 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()) {
@@ -56,9 +85,10 @@
   return false;
 }
 
+
 // byte_size is the size of the associated virtualspace.
-VirtualSpaceNode::VirtualSpaceNode(bool is_class, size_t bytes) :
-    _next(NULL), _is_class(is_class), _rs(), _top(NULL), _container_count(0), _occupancy_map(NULL) {
+VirtualSpaceNode::VirtualSpaceNode(size_t bytes) :
+    _next(NULL), _rs(), _top(NULL) {
   assert_is_aligned(bytes, Metaspace::reserve_alignment());
   bool large_pages = should_commit_large_pages_when_reserving(bytes);
   _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages);
@@ -73,16 +103,36 @@
   }
 }
 
+VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs) : _next(NULL), _rs(rs), _top(NULL) {}
+
+// Checks if the node can be purged.
+// This iterates through the chunks and checks if all are free.
+// This should be quite fast since if all chunks are free they should have been crystallized to 1-2 root chunks
+// (a non-class node is only a few MB itself). For class space, it makes no sense to call this since it cannot
+// be purged anyway.
+bool VirtualSpaceNode::purgable() const {
+  Metachunk* chunk = first_chunk();
+  Metachunk* invalid_chunk = (Metachunk*) top();
+  while (chunk < invalid_chunk ) {
+    if (chunk->is_free() == false) {
+      return false;
+    }
+    MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
+    chunk = (Metachunk*) next;
+  }
+  return true;
+}
+
 void VirtualSpaceNode::purge(ChunkManager* chunk_manager) {
   // 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");
+    assert(chunk->is_free(), "Should be free");
     MetaWord* next = ((MetaWord*)chunk) + chunk->word_size();
     chunk_manager->remove_chunk(chunk);
-    chunk->remove_sentinel();
+    DEBUG_ONLY(chunk->remove_sentinel();)
     assert(chunk->next() == NULL &&
         chunk->prev() == NULL,
         "Was not removed from its list");
@@ -91,7 +141,7 @@
 }
 
 void VirtualSpaceNode::print_map(outputStream* st, bool is_class) const {
-
+/*
   if (bottom() == top()) {
     return;
   }
@@ -168,7 +218,7 @@
   }
   for (int i = 0; i < NUM_LINES; i ++) {
     os::free(lines[i]);
-  }
+  }*/
 }
 
 
@@ -266,235 +316,60 @@
 }
 #endif // ASSERT
 
-void VirtualSpaceNode::inc_container_count() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  _container_count++;
-}
-
-void VirtualSpaceNode::dec_container_count() {
-  assert_lock_strong(MetaspaceExpand_lock);
-  _container_count--;
-}
-
 VirtualSpaceNode::~VirtualSpaceNode() {
   _rs.release();
-  if (_occupancy_map != NULL) {
-    delete _occupancy_map;
-  }
-#ifdef ASSERT
-  size_t word_size = sizeof(*this) / BytesPerWord;
-  Copy::fill_to_words((HeapWord*) this, word_size, 0xf1f1f1f1);
-#endif
-}
-
-size_t VirtualSpaceNode::used_words_in_vs() const {
-  return pointer_delta(top(), bottom(), sizeof(MetaWord));
-}
-
-// Space committed in the VirtualSpace
-size_t VirtualSpaceNode::capacity_words_in_vs() const {
-  return pointer_delta(end(), bottom(), sizeof(MetaWord));
-}
-
-size_t VirtualSpaceNode::free_words_in_vs() const {
-  return pointer_delta(end(), top(), sizeof(MetaWord));
 }
 
-// Given an address larger than top(), allocate padding chunks until top is at the given address.
-void VirtualSpaceNode::allocate_padding_chunks_until_top_is_at(MetaWord* target_top) {
-
-  assert(target_top > top(), "Sanity");
+// Allocate a root chunk (a chunk of max. size) from the the virtual space and add it to the
+// specified chunk manager as free chunk.
+void VirtualSpaceNode::allocate_new_chunk(ChunkManager* chunk_manager) {
 
-  // Padding chunks are added to the freelist.
-  ChunkManager* const chunk_manager = Metaspace::get_chunk_manager(is_class());
-
-  // shorthands
-  const size_t spec_word_size = chunk_manager->specialized_chunk_word_size();
-  const size_t small_word_size = chunk_manager->small_chunk_word_size();
-  const size_t med_word_size = chunk_manager->medium_chunk_word_size();
+  assert_is_aligned(top(), MAX_CHUNK_BYTE_SIZE);
 
-  while (top() < target_top) {
+  assert_is_aligned(uncommitted_words(), MAX_CHUNK_WORD_SIZE);
+  assert_is_aligned(unused_words(), MAX_CHUNK_WORD_SIZE);
+  assert_is_aligned(used_words(), MAX_CHUNK_WORD_SIZE);
 
-    // We could make this coding more generic, but right now we only deal with two possible chunk sizes
-    // for padding chunks, so it is not worth it.
-    size_t padding_chunk_word_size = small_word_size;
-    if (is_aligned(top(), small_word_size * sizeof(MetaWord)) == false) {
-      assert_is_aligned(top(), spec_word_size * sizeof(MetaWord)); // Should always hold true.
-      padding_chunk_word_size = spec_word_size;
-    }
-    MetaWord* here = top();
-    assert_is_aligned(here, padding_chunk_word_size * sizeof(MetaWord));
-    inc_top(padding_chunk_word_size);
+  // Caller must check, before calling this method, if node needs expansion.
+  assert(unused_words() >= MAX_CHUNK_WORD_SIZE, "Needs expansion.");
+
+  // Create new root chunk
+  MetaWord* loc = top();
+  inc_top(MAX_CHUNK_WORD_SIZE);
+  Metachunk* new_chunk = ::new (loc) Metachunk(HIGHEST_CHUNK_LEVEL, this, true);
 
-    // Create new padding chunk.
-    ChunkIndex padding_chunk_type = get_chunk_type_by_size(padding_chunk_word_size, is_class());
-    assert(padding_chunk_type == SpecializedIndex || padding_chunk_type == SmallIndex, "sanity");
+  // Add it to the chunk manager
+  new_chunk->set_in_use();
+  chunk_manager->return_chunk(new_chunk);
 
-    Metachunk* const padding_chunk =
-        ::new (here) Metachunk(padding_chunk_type, is_class(), padding_chunk_word_size, this);
-    assert(padding_chunk == (Metachunk*)here, "Sanity");
-    DEBUG_ONLY(padding_chunk->set_origin(origin_pad);)
-    log_trace(gc, metaspace, freelist)("Created padding chunk in %s at "
-        PTR_FORMAT ", size " SIZE_FORMAT_HEX ".",
-        (is_class() ? "class space " : "metaspace"),
-        p2i(padding_chunk), padding_chunk->word_size() * sizeof(MetaWord));
+}
+
+// Expands the committed portion of this node by the size of a root chunk. Will assert
+// if expansion is impossible.
+bool VirtualSpaceNode::expand() {
 
-    // Mark chunk start in occupancy map.
-    occupancy_map()->set_chunk_starts_at_address((MetaWord*)padding_chunk, true);
+  assert_is_aligned(uncommitted_words(), MAX_CHUNK_WORD_SIZE);
 
-    // Chunks are born as in-use (see MetaChunk ctor). So, before returning
-    // the padding chunk to its chunk manager, mark it as in use (ChunkManager
-    // will assert that).
-    do_update_in_use_info_for_chunk(padding_chunk, true);
+  // Caller must check, before calling this method, if node needs expansion.
+  assert(uncommitted_words() >= MAX_CHUNK_WORD_SIZE, "Node used up completely.");
 
-    // Return Chunk to freelist.
-    inc_container_count();
-    chunk_manager->return_single_chunk(padding_chunk);
-    // Please note: at this point, ChunkManager::return_single_chunk()
-    // may already have merged the padding chunk with neighboring chunks, so
-    // it may have vanished at this point. Do not reference the padding
-    // chunk beyond this point.
+  bool result = virtual_space()->expand_by(MAX_CHUNK_BYTE_SIZE, false);
+
+  if (result) {
+    log_trace(gc, metaspace, freelist)("Expanded virtual space list node by " SIZE_FORMAT " words.", MAX_CHUNK_BYTE_SIZE);
+    DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_committed_space_expanded));
+  } else {
+    log_trace(gc, metaspace, freelist)("Failed to expand virtual space list node by " SIZE_FORMAT " words.", MAX_CHUNK_BYTE_SIZE);
   }
 
-  assert(top() == target_top, "Sanity");
-
-} // allocate_padding_chunks_until_top_is_at()
-
-// Allocates the chunk from the virtual space only.
-// This interface is also used internally for debugging.  Not all
-// chunks removed here are necessarily used for allocation.
-Metachunk* VirtualSpaceNode::take_from_committed(size_t chunk_word_size) {
-  // Non-humongous chunks are to be allocated aligned to their chunk
-  // size. So, start addresses of medium chunks are aligned to medium
-  // chunk size, those of small chunks to small chunk size and so
-  // forth. This facilitates merging of free chunks and reduces
-  // fragmentation. Chunk sizes are spec < small < medium, with each
-  // larger chunk size being a multiple of the next smaller chunk
-  // size.
-  // Because of this alignment, me may need to create a number of padding
-  // 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(is_class());
-
-  // shorthands
-  const size_t spec_word_size = chunk_manager->specialized_chunk_word_size();
-  const size_t small_word_size = chunk_manager->small_chunk_word_size();
-  const size_t med_word_size = chunk_manager->medium_chunk_word_size();
-
-  assert(chunk_word_size == spec_word_size || chunk_word_size == small_word_size ||
-      chunk_word_size >= med_word_size, "Invalid chunk size requested.");
-
-  // Chunk alignment (in bytes) == chunk size unless humongous.
-  // Humongous chunks are aligned to the smallest chunk size (spec).
-  const size_t required_chunk_alignment = (chunk_word_size > med_word_size ?
-      spec_word_size : chunk_word_size) * sizeof(MetaWord);
-
-  // Do we have enough space to create the requested chunk plus
-  // any padding chunks needed?
-  MetaWord* const next_aligned =
-      static_cast<MetaWord*>(align_up(top(), required_chunk_alignment));
-  if (!is_available((next_aligned - top()) + chunk_word_size)) {
-    return NULL;
-  }
+  assert(result, "Failed to commit memory");
 
-  // Before allocating the requested chunk, allocate padding chunks if necessary.
-  // We only need to do this for small or medium chunks: specialized chunks are the
-  // smallest size, hence always aligned. Homungous chunks are allocated unaligned
-  // (implicitly, also aligned to smallest chunk size).
-  if ((chunk_word_size == med_word_size || chunk_word_size == small_word_size) && next_aligned > top())  {
-    log_trace(gc, metaspace, freelist)("Creating padding chunks in %s between %p and %p...",
-        (is_class() ? "class space " : "metaspace"),
-        top(), next_aligned);
-    allocate_padding_chunks_until_top_is_at(next_aligned);
-    // Now, top should be aligned correctly.
-    assert_is_aligned(top(), required_chunk_alignment);
-  }
-
-  // Now, top should be aligned correctly.
-  assert_is_aligned(top(), required_chunk_alignment);
-
-  // Bottom of the new chunk
-  MetaWord* chunk_limit = top();
-  assert(chunk_limit != NULL, "Not safe to call this method");
-
-  // The virtual spaces are always expanded by the
-  // commit granularity to enforce the following condition.
-  // Without this the is_available check will not work correctly.
-  assert(_virtual_space.committed_size() == _virtual_space.actual_committed_size(),
-      "The committed memory doesn't match the expanded memory.");
-
-  if (!is_available(chunk_word_size)) {
-    LogTarget(Trace, gc, metaspace, freelist) lt;
-    if (lt.is_enabled()) {
-      LogStream ls(lt);
-      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);
-    }
-    return NULL;
-  }
-
-  // Take the space  (bump top on the current virtual space).
-  inc_top(chunk_word_size);
-
-  // Initialize the chunk
-  ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class());
-  Metachunk* result = ::new (chunk_limit) Metachunk(chunk_type, is_class(), chunk_word_size, this);
-  assert(result == (Metachunk*)chunk_limit, "Sanity");
-  occupancy_map()->set_chunk_starts_at_address((MetaWord*)result, true);
-  do_update_in_use_info_for_chunk(result, true);
-
-  inc_container_count();
-
-#ifdef ASSERT
-  EVERY_NTH(VerifyMetaspaceInterval)
-    chunk_manager->locked_verify(true);
-    verify(true);
-  END_EVERY_NTH
-  do_verify_chunk(result);
-#endif
-
-  result->inc_use_count();
+  assert(unused_words() >= MAX_CHUNK_WORD_SIZE, "sanity");
 
   return result;
 }
 
 
-// Expand the virtual space (commit more of the reserved space)
-bool VirtualSpaceNode::expand_by(size_t min_words, size_t preferred_words) {
-  size_t min_bytes = min_words * BytesPerWord;
-  size_t preferred_bytes = preferred_words * BytesPerWord;
-
-  size_t uncommitted = virtual_space()->reserved_size() - virtual_space()->actual_committed_size();
-
-  if (uncommitted < min_bytes) {
-    return false;
-  }
-
-  size_t commit = MIN2(preferred_bytes, uncommitted);
-  bool result = virtual_space()->expand_by(commit, false);
-
-  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);
-  }
-
-  assert(result, "Failed to commit memory");
-
-  return result;
-}
-
-Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) {
-  assert_lock_strong(MetaspaceExpand_lock);
-  Metachunk* result = take_from_committed(chunk_word_size);
-  return result;
-}
-
 bool VirtualSpaceNode::initialize() {
 
   if (!_rs.is_reserved()) {
@@ -521,10 +396,6 @@
     set_top((MetaWord*)virtual_space()->low());
   }
 
-  // Initialize Occupancy Map.
-  const size_t smallest_chunk_size = is_class() ? ClassSpecializedChunk : SpecializedChunk;
-  _occupancy_map = new OccupancyMap(bottom(), reserved_words(), smallest_chunk_size);
-
   return result;
 }