8198511: Move allocation functions from GenCollectorPolicy to GenCollectedHeap
authorstefank
Thu, 22 Feb 2018 18:35:40 +0100
changeset 49046 c8e4dc1b9e39
parent 49045 9b556b613a07
child 49047 8f004146e407
8198511: Move allocation functions from GenCollectorPolicy to GenCollectedHeap Reviewed-by: pliden, sjohanss
src/hotspot/share/gc/shared/collectorPolicy.cpp
src/hotspot/share/gc/shared/collectorPolicy.hpp
src/hotspot/share/gc/shared/genCollectedHeap.cpp
src/hotspot/share/gc/shared/genCollectedHeap.hpp
--- a/src/hotspot/share/gc/shared/collectorPolicy.cpp	Thu Feb 22 18:35:04 2018 +0100
+++ b/src/hotspot/share/gc/shared/collectorPolicy.cpp	Thu Feb 22 18:35:40 2018 +0100
@@ -576,245 +576,6 @@
   DEBUG_ONLY(GenCollectorPolicy::assert_size_info();)
 }
 
-HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,
-                                        bool is_tlab,
-                                        bool* gc_overhead_limit_was_exceeded) {
-  GenCollectedHeap *gch = GenCollectedHeap::heap();
-
-  debug_only(gch->check_for_valid_allocation_state());
-  assert(gch->no_gc_in_progress(), "Allocation during gc not allowed");
-
-  // In general gc_overhead_limit_was_exceeded should be false so
-  // set it so here and reset it to true only if the gc time
-  // limit is being exceeded as checked below.
-  *gc_overhead_limit_was_exceeded = false;
-
-  HeapWord* result = NULL;
-
-  // Loop until the allocation is satisfied, or unsatisfied after GC.
-  for (uint try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
-    HandleMark hm; // Discard any handles allocated in each iteration.
-
-    // First allocation attempt is lock-free.
-    Generation *young = gch->young_gen();
-    assert(young->supports_inline_contig_alloc(),
-      "Otherwise, must do alloc within heap lock");
-    if (young->should_allocate(size, is_tlab)) {
-      result = young->par_allocate(size, is_tlab);
-      if (result != NULL) {
-        assert(gch->is_in_reserved(result), "result not in heap");
-        return result;
-      }
-    }
-    uint gc_count_before;  // Read inside the Heap_lock locked region.
-    {
-      MutexLocker ml(Heap_lock);
-      log_trace(gc, alloc)("GenCollectorPolicy::mem_allocate_work: attempting locked slow path allocation");
-      // Note that only large objects get a shot at being
-      // allocated in later generations.
-      bool first_only = ! should_try_older_generation_allocation(size);
-
-      result = gch->attempt_allocation(size, is_tlab, first_only);
-      if (result != NULL) {
-        assert(gch->is_in_reserved(result), "result not in heap");
-        return result;
-      }
-
-      if (GCLocker::is_active_and_needs_gc()) {
-        if (is_tlab) {
-          return NULL;  // Caller will retry allocating individual object.
-        }
-        if (!gch->is_maximal_no_gc()) {
-          // Try and expand heap to satisfy request.
-          result = expand_heap_and_allocate(size, is_tlab);
-          // Result could be null if we are out of space.
-          if (result != NULL) {
-            return result;
-          }
-        }
-
-        if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
-          return NULL; // We didn't get to do a GC and we didn't get any memory.
-        }
-
-        // If this thread is not in a jni critical section, we stall
-        // the requestor until the critical section has cleared and
-        // GC allowed. When the critical section clears, a GC is
-        // initiated by the last thread exiting the critical section; so
-        // we retry the allocation sequence from the beginning of the loop,
-        // rather than causing more, now probably unnecessary, GC attempts.
-        JavaThread* jthr = JavaThread::current();
-        if (!jthr->in_critical()) {
-          MutexUnlocker mul(Heap_lock);
-          // Wait for JNI critical section to be exited
-          GCLocker::stall_until_clear();
-          gclocker_stalled_count += 1;
-          continue;
-        } else {
-          if (CheckJNICalls) {
-            fatal("Possible deadlock due to allocating while"
-                  " in jni critical section");
-          }
-          return NULL;
-        }
-      }
-
-      // Read the gc count while the heap lock is held.
-      gc_count_before = gch->total_collections();
-    }
-
-    VM_GenCollectForAllocation op(size, is_tlab, gc_count_before);
-    VMThread::execute(&op);
-    if (op.prologue_succeeded()) {
-      result = op.result();
-      if (op.gc_locked()) {
-         assert(result == NULL, "must be NULL if gc_locked() is true");
-         continue;  // Retry and/or stall as necessary.
-      }
-
-      // Allocation has failed and a collection
-      // has been done.  If the gc time limit was exceeded the
-      // this time, return NULL so that an out-of-memory
-      // will be thrown.  Clear gc_overhead_limit_exceeded
-      // so that the overhead exceeded does not persist.
-
-      const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
-      const bool softrefs_clear = all_soft_refs_clear();
-
-      if (limit_exceeded && softrefs_clear) {
-        *gc_overhead_limit_was_exceeded = true;
-        size_policy()->set_gc_overhead_limit_exceeded(false);
-        if (op.result() != NULL) {
-          CollectedHeap::fill_with_object(op.result(), size);
-        }
-        return NULL;
-      }
-      assert(result == NULL || gch->is_in_reserved(result),
-             "result not in heap");
-      return result;
-    }
-
-    // Give a warning if we seem to be looping forever.
-    if ((QueuedAllocationWarningCount > 0) &&
-        (try_count % QueuedAllocationWarningCount == 0)) {
-          log_warning(gc, ergo)("GenCollectorPolicy::mem_allocate_work retries %d times,"
-                                " size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
-    }
-  }
-}
-
-HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size,
-                                                       bool   is_tlab) {
-  GenCollectedHeap *gch = GenCollectedHeap::heap();
-  HeapWord* result = NULL;
-  Generation *old = gch->old_gen();
-  if (old->should_allocate(size, is_tlab)) {
-    result = old->expand_and_allocate(size, is_tlab);
-  }
-  if (result == NULL) {
-    Generation *young = gch->young_gen();
-    if (young->should_allocate(size, is_tlab)) {
-      result = young->expand_and_allocate(size, is_tlab);
-    }
-  }
-  assert(result == NULL || gch->is_in_reserved(result), "result not in heap");
-  return result;
-}
-
-HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
-                                                        bool   is_tlab) {
-  GenCollectedHeap *gch = GenCollectedHeap::heap();
-  GCCauseSetter x(gch, GCCause::_allocation_failure);
-  HeapWord* result = NULL;
-
-  assert(size != 0, "Precondition violated");
-  if (GCLocker::is_active_and_needs_gc()) {
-    // GC locker is active; instead of a collection we will attempt
-    // to expand the heap, if there's room for expansion.
-    if (!gch->is_maximal_no_gc()) {
-      result = expand_heap_and_allocate(size, is_tlab);
-    }
-    return result;   // Could be null if we are out of space.
-  } else if (!gch->incremental_collection_will_fail(false /* don't consult_young */)) {
-    // Do an incremental collection.
-    gch->do_collection(false,                     // full
-                       false,                     // clear_all_soft_refs
-                       size,                      // size
-                       is_tlab,                   // is_tlab
-                       GenCollectedHeap::OldGen); // max_generation
-  } else {
-    log_trace(gc)(" :: Trying full because partial may fail :: ");
-    // Try a full collection; see delta for bug id 6266275
-    // for the original code and why this has been simplified
-    // with from-space allocation criteria modified and
-    // such allocation moved out of the safepoint path.
-    gch->do_collection(true,                      // full
-                       false,                     // clear_all_soft_refs
-                       size,                      // size
-                       is_tlab,                   // is_tlab
-                       GenCollectedHeap::OldGen); // max_generation
-  }
-
-  result = gch->attempt_allocation(size, is_tlab, false /*first_only*/);
-
-  if (result != NULL) {
-    assert(gch->is_in_reserved(result), "result not in heap");
-    return result;
-  }
-
-  // OK, collection failed, try expansion.
-  result = expand_heap_and_allocate(size, is_tlab);
-  if (result != NULL) {
-    return result;
-  }
-
-  // If we reach this point, we're really out of memory. Try every trick
-  // we can to reclaim memory. Force collection of soft references. Force
-  // a complete compaction of the heap. Any additional methods for finding
-  // free memory should be here, especially if they are expensive. If this
-  // attempt fails, an OOM exception will be thrown.
-  {
-    UIntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted
-
-    gch->do_collection(true,                      // full
-                       true,                      // clear_all_soft_refs
-                       size,                      // size
-                       is_tlab,                   // is_tlab
-                       GenCollectedHeap::OldGen); // max_generation
-  }
-
-  result = gch->attempt_allocation(size, is_tlab, false /* first_only */);
-  if (result != NULL) {
-    assert(gch->is_in_reserved(result), "result not in heap");
-    return result;
-  }
-
-  assert(!should_clear_all_soft_refs(),
-    "Flag should have been handled and cleared prior to this point");
-
-  // What else?  We might try synchronous finalization later.  If the total
-  // space available is large enough for the allocation, then a more
-  // complete compaction phase than we've tried so far might be
-  // appropriate.
-  return NULL;
-}
-
-// Return true if any of the following is true:
-// . the allocation won't fit into the current young gen heap
-// . gc locker is occupied (jni critical section)
-// . heap memory is tight -- the most recent previous collection
-//   was a full collection because a partial collection (would
-//   have) failed and is likely to fail again
-bool GenCollectorPolicy::should_try_older_generation_allocation(
-        size_t word_size) const {
-  GenCollectedHeap* gch = GenCollectedHeap::heap();
-  size_t young_capacity = gch->young_gen()->capacity_before_gc();
-  return    (word_size > heap_word_size(young_capacity))
-         || GCLocker::is_active_and_needs_gc()
-         || gch->incremental_collection_failed();
-}
-
-
 //
 // MarkSweepPolicy methods
 //
@@ -833,4 +594,3 @@
   // Initialize the policy counters - 2 collectors, 2 generations.
   _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 2);
 }
-
--- a/src/hotspot/share/gc/shared/collectorPolicy.hpp	Thu Feb 22 18:35:04 2018 +0100
+++ b/src/hotspot/share/gc/shared/collectorPolicy.hpp	Thu Feb 22 18:35:40 2018 +0100
@@ -157,19 +157,12 @@
   // The sizing of the heap is controlled by a sizing policy.
   AdaptiveSizePolicy* _size_policy;
 
-  // Return true if an allocation should be attempted in the older generation
-  // if it fails in the younger generation.  Return false, otherwise.
-  virtual bool should_try_older_generation_allocation(size_t word_size) const;
-
   void initialize_flags();
   void initialize_size_info();
 
   DEBUG_ONLY(void assert_flags();)
   DEBUG_ONLY(void assert_size_info();)
 
-  // Try to allocate space by expanding the heap.
-  virtual HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab);
-
   // Compute max heap alignment.
   size_t compute_max_alignment();
 
@@ -220,12 +213,6 @@
 
   size_t old_gen_size_lower_bound();
 
-  HeapWord* mem_allocate_work(size_t size,
-                              bool is_tlab,
-                              bool* gc_overhead_limit_was_exceeded);
-
-  HeapWord *satisfy_failed_allocation(size_t size, bool is_tlab);
-
   // Adaptive size policy
   AdaptiveSizePolicy* size_policy() { return _size_policy; }
 
--- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp	Thu Feb 22 18:35:04 2018 +0100
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp	Thu Feb 22 18:35:40 2018 +0100
@@ -203,6 +203,157 @@
   return _full_collections_completed;
 }
 
+// Return true if any of the following is true:
+// . the allocation won't fit into the current young gen heap
+// . gc locker is occupied (jni critical section)
+// . heap memory is tight -- the most recent previous collection
+//   was a full collection because a partial collection (would
+//   have) failed and is likely to fail again
+bool GenCollectedHeap::should_try_older_generation_allocation(size_t word_size) const {
+  size_t young_capacity = young_gen()->capacity_before_gc();
+  return    (word_size > heap_word_size(young_capacity))
+         || GCLocker::is_active_and_needs_gc()
+         || incremental_collection_failed();
+}
+
+HeapWord* GenCollectedHeap::expand_heap_and_allocate(size_t size, bool   is_tlab) {
+  HeapWord* result = NULL;
+  if (old_gen()->should_allocate(size, is_tlab)) {
+    result = old_gen()->expand_and_allocate(size, is_tlab);
+  }
+  if (result == NULL) {
+    if (young_gen()->should_allocate(size, is_tlab)) {
+      result = young_gen()->expand_and_allocate(size, is_tlab);
+    }
+  }
+  assert(result == NULL || is_in_reserved(result), "result not in heap");
+  return result;
+}
+
+HeapWord* GenCollectedHeap::mem_allocate_work(size_t size,
+                                              bool is_tlab,
+                                              bool* gc_overhead_limit_was_exceeded) {
+  debug_only(check_for_valid_allocation_state());
+  assert(no_gc_in_progress(), "Allocation during gc not allowed");
+
+  // In general gc_overhead_limit_was_exceeded should be false so
+  // set it so here and reset it to true only if the gc time
+  // limit is being exceeded as checked below.
+  *gc_overhead_limit_was_exceeded = false;
+
+  HeapWord* result = NULL;
+
+  // Loop until the allocation is satisfied, or unsatisfied after GC.
+  for (uint try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
+    HandleMark hm; // Discard any handles allocated in each iteration.
+
+    // First allocation attempt is lock-free.
+    Generation *young = young_gen();
+    assert(young->supports_inline_contig_alloc(),
+      "Otherwise, must do alloc within heap lock");
+    if (young->should_allocate(size, is_tlab)) {
+      result = young->par_allocate(size, is_tlab);
+      if (result != NULL) {
+        assert(is_in_reserved(result), "result not in heap");
+        return result;
+      }
+    }
+    uint gc_count_before;  // Read inside the Heap_lock locked region.
+    {
+      MutexLocker ml(Heap_lock);
+      log_trace(gc, alloc)("GenCollectedHeap::mem_allocate_work: attempting locked slow path allocation");
+      // Note that only large objects get a shot at being
+      // allocated in later generations.
+      bool first_only = !should_try_older_generation_allocation(size);
+
+      result = attempt_allocation(size, is_tlab, first_only);
+      if (result != NULL) {
+        assert(is_in_reserved(result), "result not in heap");
+        return result;
+      }
+
+      if (GCLocker::is_active_and_needs_gc()) {
+        if (is_tlab) {
+          return NULL;  // Caller will retry allocating individual object.
+        }
+        if (!is_maximal_no_gc()) {
+          // Try and expand heap to satisfy request.
+          result = expand_heap_and_allocate(size, is_tlab);
+          // Result could be null if we are out of space.
+          if (result != NULL) {
+            return result;
+          }
+        }
+
+        if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
+          return NULL; // We didn't get to do a GC and we didn't get any memory.
+        }
+
+        // If this thread is not in a jni critical section, we stall
+        // the requestor until the critical section has cleared and
+        // GC allowed. When the critical section clears, a GC is
+        // initiated by the last thread exiting the critical section; so
+        // we retry the allocation sequence from the beginning of the loop,
+        // rather than causing more, now probably unnecessary, GC attempts.
+        JavaThread* jthr = JavaThread::current();
+        if (!jthr->in_critical()) {
+          MutexUnlocker mul(Heap_lock);
+          // Wait for JNI critical section to be exited
+          GCLocker::stall_until_clear();
+          gclocker_stalled_count += 1;
+          continue;
+        } else {
+          if (CheckJNICalls) {
+            fatal("Possible deadlock due to allocating while"
+                  " in jni critical section");
+          }
+          return NULL;
+        }
+      }
+
+      // Read the gc count while the heap lock is held.
+      gc_count_before = total_collections();
+    }
+
+    VM_GenCollectForAllocation op(size, is_tlab, gc_count_before);
+    VMThread::execute(&op);
+    if (op.prologue_succeeded()) {
+      result = op.result();
+      if (op.gc_locked()) {
+         assert(result == NULL, "must be NULL if gc_locked() is true");
+         continue;  // Retry and/or stall as necessary.
+      }
+
+      // Allocation has failed and a collection
+      // has been done.  If the gc time limit was exceeded the
+      // this time, return NULL so that an out-of-memory
+      // will be thrown.  Clear gc_overhead_limit_exceeded
+      // so that the overhead exceeded does not persist.
+
+      const bool limit_exceeded = gen_policy()->size_policy()->gc_overhead_limit_exceeded();
+      const bool softrefs_clear = gen_policy()->all_soft_refs_clear();
+
+      if (limit_exceeded && softrefs_clear) {
+        *gc_overhead_limit_was_exceeded = true;
+        gen_policy()->size_policy()->set_gc_overhead_limit_exceeded(false);
+        if (op.result() != NULL) {
+          CollectedHeap::fill_with_object(op.result(), size);
+        }
+        return NULL;
+      }
+      assert(result == NULL || is_in_reserved(result),
+             "result not in heap");
+      return result;
+    }
+
+    // Give a warning if we seem to be looping forever.
+    if ((QueuedAllocationWarningCount > 0) &&
+        (try_count % QueuedAllocationWarningCount == 0)) {
+          log_warning(gc, ergo)("GenCollectedHeap::mem_allocate_work retries %d times,"
+                                " size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
+    }
+  }
+}
 
 #ifndef PRODUCT
 // Override of memory state checking method in CollectedHeap:
@@ -254,9 +405,9 @@
 
 HeapWord* GenCollectedHeap::mem_allocate(size_t size,
                                          bool* gc_overhead_limit_was_exceeded) {
-  return gen_policy()->mem_allocate_work(size,
-                                         false /* is_tlab */,
-                                         gc_overhead_limit_was_exceeded);
+  return mem_allocate_work(size,
+                           false /* is_tlab */,
+                           gc_overhead_limit_was_exceeded);
 }
 
 bool GenCollectedHeap::must_clear_all_soft_refs() {
@@ -504,7 +655,79 @@
 }
 
 HeapWord* GenCollectedHeap::satisfy_failed_allocation(size_t size, bool is_tlab) {
-  return gen_policy()->satisfy_failed_allocation(size, is_tlab);
+  GCCauseSetter x(this, GCCause::_allocation_failure);
+  HeapWord* result = NULL;
+
+  assert(size != 0, "Precondition violated");
+  if (GCLocker::is_active_and_needs_gc()) {
+    // GC locker is active; instead of a collection we will attempt
+    // to expand the heap, if there's room for expansion.
+    if (!is_maximal_no_gc()) {
+      result = expand_heap_and_allocate(size, is_tlab);
+    }
+    return result;   // Could be null if we are out of space.
+  } else if (!incremental_collection_will_fail(false /* don't consult_young */)) {
+    // Do an incremental collection.
+    do_collection(false,                     // full
+                  false,                     // clear_all_soft_refs
+                  size,                      // size
+                  is_tlab,                   // is_tlab
+                  GenCollectedHeap::OldGen); // max_generation
+  } else {
+    log_trace(gc)(" :: Trying full because partial may fail :: ");
+    // Try a full collection; see delta for bug id 6266275
+    // for the original code and why this has been simplified
+    // with from-space allocation criteria modified and
+    // such allocation moved out of the safepoint path.
+    do_collection(true,                      // full
+                  false,                     // clear_all_soft_refs
+                  size,                      // size
+                  is_tlab,                   // is_tlab
+                  GenCollectedHeap::OldGen); // max_generation
+  }
+
+  result = attempt_allocation(size, is_tlab, false /*first_only*/);
+
+  if (result != NULL) {
+    assert(is_in_reserved(result), "result not in heap");
+    return result;
+  }
+
+  // OK, collection failed, try expansion.
+  result = expand_heap_and_allocate(size, is_tlab);
+  if (result != NULL) {
+    return result;
+  }
+
+  // If we reach this point, we're really out of memory. Try every trick
+  // we can to reclaim memory. Force collection of soft references. Force
+  // a complete compaction of the heap. Any additional methods for finding
+  // free memory should be here, especially if they are expensive. If this
+  // attempt fails, an OOM exception will be thrown.
+  {
+    UIntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted
+
+    do_collection(true,                      // full
+                  true,                      // clear_all_soft_refs
+                  size,                      // size
+                  is_tlab,                   // is_tlab
+                  GenCollectedHeap::OldGen); // max_generation
+  }
+
+  result = attempt_allocation(size, is_tlab, false /* first_only */);
+  if (result != NULL) {
+    assert(is_in_reserved(result), "result not in heap");
+    return result;
+  }
+
+  assert(!gen_policy()->should_clear_all_soft_refs(),
+    "Flag should have been handled and cleared prior to this point");
+
+  // What else?  We might try synchronous finalization later.  If the total
+  // space available is large enough for the allocation, then a more
+  // complete compaction phase than we've tried so far might be
+  // appropriate.
+  return NULL;
 }
 
 #ifdef ASSERT
@@ -887,9 +1110,9 @@
 
 HeapWord* GenCollectedHeap::allocate_new_tlab(size_t size) {
   bool gc_overhead_limit_was_exceeded;
-  return gen_policy()->mem_allocate_work(size /* size */,
-                                         true /* is_tlab */,
-                                         &gc_overhead_limit_was_exceeded);
+  return mem_allocate_work(size /* size */,
+                           true /* is_tlab */,
+                           &gc_overhead_limit_was_exceeded);
 }
 
 // Requires "*prev_ptr" to be non-NULL.  Deletes and a block of minimal size
--- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp	Thu Feb 22 18:35:04 2018 +0100
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp	Thu Feb 22 18:35:40 2018 +0100
@@ -456,6 +456,17 @@
 
 
 private:
+  // Return true if an allocation should be attempted in the older generation
+  // if it fails in the younger generation.  Return false, otherwise.
+  bool should_try_older_generation_allocation(size_t word_size) const;
+
+  // Try to allocate space by expanding the heap.
+  HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab);
+
+  HeapWord* mem_allocate_work(size_t size,
+                              bool is_tlab,
+                              bool* gc_overhead_limit_was_exceeded);
+
   // Override
   void check_for_non_bad_heap_word_value(HeapWord* addr,
     size_t size) PRODUCT_RETURN;