< prev index next >

src/hotspot/share/memory/heap.cpp

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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.

@@ -53,21 +53,29 @@
   _adapter_count                = 0;
   _full_count                   = 0;
 }
 
 
+// The segmap is marked free for that part of the heap
+// which has not been allocated yet (beyond _next_segment).
+// "Allocated" space in this context means there exists a
+// HeapBlock or a FreeBlock describing this space.
 void CodeHeap::mark_segmap_as_free(size_t beg, size_t end) {
   assert(              beg <  _number_of_committed_segments, "interval begin out of bounds");
   assert(beg <  end && end <= _number_of_committed_segments, "interval end   out of bounds");
   // setup _segmap pointers for faster indexing
   address p = (address)_segmap.low() + beg;
   address q = (address)_segmap.low() + end;
   // initialize interval
-  while (p < q) *p++ = free_sentinel;
+  memset(p, free_sentinel, q-p);
 }
 
-
+// Don't get confused here.
+// All existing blocks, no matter if they are used() or free(),
+// have their segmap marked as used. This allows to find the
+// block header (HeapBlock or FreeBlock) for any pointer
+// within the allocated range (upper limit: _next_segment).
 void CodeHeap::mark_segmap_as_used(size_t beg, size_t end) {
   assert(              beg <  _number_of_committed_segments, "interval begin out of bounds");
   assert(beg <  end && end <= _number_of_committed_segments, "interval end   out of bounds");
   // setup _segmap pointers for faster indexing
   address p = (address)_segmap.low() + beg;

@@ -187,11 +195,10 @@
   NOT_PRODUCT(verify());
   HeapBlock* block = search_freelist(number_of_segments);
   NOT_PRODUCT(verify());
 
   if (block != NULL) {
-    assert(block->length() >= number_of_segments && block->length() < number_of_segments + CodeCacheMinBlockLength, "sanity check");
     assert(!block->free(), "must be marked free");
     guarantee((char*) block >= _memory.low_boundary() && (char*) block < _memory.high(),
               "The newly allocated block " INTPTR_FORMAT " is not within the heap "
               "starting with "  INTPTR_FORMAT " and ending with "  INTPTR_FORMAT,
               p2i(block), p2i(_memory.low_boundary()), p2i(_memory.high()));

@@ -220,23 +227,51 @@
   } else {
     return NULL;
   }
 }
 
+// Split the given block into two at the given segment.
+// This is helpful when a block was allocated too large
+// to trim off the unused space at the end (interpreter).
+// It also helps with splitting a large free block during allocation.
+// Usage state (used or free) must be set by caller since
+// we don't know if the resulting blocks will be used or free.
+// split_at is the segment number (relative to segment_for(b))
+//          where the split happens. The segment with relative
+//          number split_at is the first segment of the split-off block.
+HeapBlock* CodeHeap::split_block(HeapBlock *b, size_t split_at) {
+  if (b == NULL) return NULL;
+  // After the split, both blocks must have a size of at least CodeCacheMinBlockLength
+  assert((split_at >= CodeCacheMinBlockLength) && (split_at + CodeCacheMinBlockLength <= b->length()),
+         "split position(%d) out of range [0..%d]", (int)split_at, (int)b->length());
+  size_t split_segment = segment_for(b) + split_at;
+  size_t b_size        = b->length();
+  size_t newb_size     = b_size - split_at;
+
+  HeapBlock* newb = block_at(split_segment);
+  newb->set_length(newb_size);
+  mark_segmap_as_used(segment_for(newb), segment_for(newb) + newb_size);
+  b->set_length(split_at);
+  return newb;
+}
+
 void CodeHeap::deallocate_tail(void* p, size_t used_size) {
   assert(p == find_start(p), "illegal deallocation");
   // Find start of HeapBlock
   HeapBlock* b = (((HeapBlock *)p) - 1);
   assert(b->allocated_space() == p, "sanity check");
-  size_t used_number_of_segments = size_to_segments(used_size + header_size());
+
   size_t actual_number_of_segments = b->length();
+  size_t used_number_of_segments   = size_to_segments(used_size + header_size());
+  size_t unused_number_of_segments = actual_number_of_segments - used_number_of_segments;
   guarantee(used_number_of_segments <= actual_number_of_segments, "Must be!");
-  guarantee(b == block_at(_next_segment - actual_number_of_segments), "Intermediate allocation!");
-  size_t number_of_segments_to_deallocate = actual_number_of_segments - used_number_of_segments;
-  _next_segment -= number_of_segments_to_deallocate;
-  mark_segmap_as_free(_next_segment, _next_segment + number_of_segments_to_deallocate);
-  b->initialize(used_number_of_segments);
+
+  HeapBlock* f = split_block(b, used_number_of_segments);
+  DEBUG_ONLY(memset((void *)f->allocated_space(), badCodeHeapFreeVal,
+             segments_to_size(unused_number_of_segments) - sizeof(HeapBlock)));
+  add_to_freelist(f);
+  NOT_PRODUCT(verify());
 }
 
 void CodeHeap::deallocate(void* p) {
   assert(p == find_start(p), "illegal deallocation");
   // Find start of HeapBlock

@@ -428,12 +463,12 @@
   _freelist_segments += b->length();
   b->set_free();
 
   // First element in list?
   if (_freelist == NULL) {
-    _freelist = b;
     b->set_link(NULL);
+    _freelist = b;
     return;
   }
 
   // Since the freelist is ordered (smaller addresses -> larger addresses) and the
   // element we want to insert into the freelist has a smaller address than the first

@@ -461,27 +496,35 @@
 
 /**
  * Search freelist for an entry on the list with the best fit.
  * @return NULL, if no one was found
  */
-FreeBlock* CodeHeap::search_freelist(size_t length) {
+HeapBlock* CodeHeap::search_freelist(size_t length) {
   FreeBlock* found_block = NULL;
   FreeBlock* found_prev  = NULL;
-  size_t     found_length = 0;
+  size_t     found_length = _next_segment; // max it out to begin with
 
+  HeapBlock* res  = NULL;
   FreeBlock* prev = NULL;
   FreeBlock* cur = _freelist;
 
-  // Search for first block that fits
+  length = length < CodeCacheMinBlockLength ? CodeCacheMinBlockLength : length;
+
+  // Search for best-fitting block
   while(cur != NULL) {
-    if (cur->length() >= length) {
-      // Remember block, its previous element, and its length
+    size_t cur_length = cur->length();
+    if (cur_length == length) {
+      // We have a perfect fit
       found_block = cur;
       found_prev  = prev;
-      found_length = found_block->length();
-
+      found_length = cur_length;
       break;
+    } else if ((cur_length > length) && (cur_length < found_length)) {
+      // This is a new, closer fit. Remember block, its previous element, and its length
+      found_block  = cur;
+      found_prev   = prev;
+      found_length = cur_length;
     }
     // Next element in list
     prev = cur;
     cur  = cur->link();
   }

@@ -502,24 +545,22 @@
     } else {
       assert((found_prev->link() == found_block), "sanity check");
       // Unmap element
       found_prev->set_link(found_block->link());
     }
+    res = found_block;
   } else {
-    // Truncate block and return a pointer to the following block
-    // Set used bit and length on new block
-    found_block->set_length(found_length - length);
-    found_block = following_block(found_block);
-
-    size_t beg = segment_for(found_block);
-    mark_segmap_as_used(beg, beg + length);
-    found_block->set_length(length);
+    // Truncate the free block and return the truncated part
+    // as new HeapBlock. The remaining free block does not
+    // need to be updated, except for it's length. Truncating
+    // the segment map does not invalidate the leading part.
+    res = split_block(found_block, found_length - length);
   }
 
-  found_block->set_used();
+  res->set_used();
   _freelist_segments -= length;
-  return found_block;
+  return res;
 }
 
 //----------------------------------------------------------------------------
 // Non-product code
 

@@ -547,10 +588,29 @@
     }
     // Verify that the freelist contains the same number of blocks
     // than free blocks found on the full list.
     assert(count == 0, "missing free blocks");
 
+    // Verify segment map marking.
+    // All allocated segments, no matter if in a free or used block,
+    // must be marked "in use".
+    address seg_map = (address)_segmap.low();
+    size_t  nseg    = 0;
+    for(HeapBlock* b = first_block(); b != NULL; b = next_block(b)) {
+      size_t seg1 = segment_for(b);
+      size_t segn = seg1 + b->length();
+      for (size_t i = seg1; i < segn; i++) {
+        nseg++;
+        if (is_segment_unused(seg_map[i])) {
+          warning("CodeHeap: unused segment. %d [%d..%d], %s block", (int)i, (int)seg1, (int)segn, b->free()? "free":"used");
+        }
+      }
+    }
+    if (nseg != _next_segment) {
+      warning("CodeHeap: segment count mismatch. found %d, expected %d.", (int)nseg, (int)_next_segment);
+    }
+
     // Verify that the number of free blocks is not out of hand.
     static int free_block_threshold = 10000;
     if (count > free_block_threshold) {
       warning("CodeHeap: # of free blocks > %d", free_block_threshold);
       // Double the warning limit
< prev index next >