8222186: Shenandoah should not uncommit below minimum heap size
authorshade
Tue, 09 Apr 2019 21:20:16 +0200
changeset 54478 cdc54443fee5
parent 54477 511be32f3863
child 54479 6ad0281a654e
child 57317 c4980b115ead
8222186: Shenandoah should not uncommit below minimum heap size Reviewed-by: zgu, rkennke
src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java
--- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp	Tue Apr 09 21:20:15 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp	Tue Apr 09 21:20:16 2019 +0200
@@ -139,6 +139,11 @@
     FLAG_SET_DEFAULT(ShenandoahUncommit, false);
   }
 
+  if ((InitialHeapSize == MaxHeapSize) && ShenandoahUncommit) {
+    log_info(gc)("Min heap equals to max heap, disabling ShenandoahUncommit");
+    FLAG_SET_DEFAULT(ShenandoahUncommit, false);
+  }
+
   // If class unloading is disabled, no unloading for concurrent cycles as well.
   // If class unloading is enabled, users should opt-in for unloading during
   // concurrent cycles.
--- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp	Tue Apr 09 21:20:15 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp	Tue Apr 09 21:20:16 2019 +0200
@@ -462,9 +462,11 @@
 void ShenandoahControlThread::service_uncommit(double shrink_before) {
   ShenandoahHeap* heap = ShenandoahHeap::heap();
 
-  // Scan through the heap and determine if there is work to do. This avoids taking
-  // heap lock if there is no work available, avoids spamming logs with superfluous
-  // logging messages, and minimises the amount of work while locks are taken.
+  // Determine if there is work to do. This avoids taking heap lock if there is
+  // no work available, avoids spamming logs with superfluous logging messages,
+  // and minimises the amount of work while locks are taken.
+
+  if (heap->committed() <= heap->min_capacity()) return;
 
   bool has_work = false;
   for (size_t i = 0; i < heap->num_regions(); i++) {
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue Apr 09 21:20:15 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue Apr 09 21:20:16 2019 +0200
@@ -141,6 +141,7 @@
   //
 
   size_t init_byte_size = collector_policy()->initial_heap_byte_size();
+  size_t min_byte_size  = collector_policy()->min_heap_byte_size();
   size_t max_byte_size  = collector_policy()->max_heap_byte_size();
   size_t heap_alignment = collector_policy()->heap_alignment();
 
@@ -159,8 +160,13 @@
   size_t num_committed_regions = init_byte_size / reg_size_bytes;
   num_committed_regions = MIN2(num_committed_regions, _num_regions);
   assert(num_committed_regions <= _num_regions, "sanity");
-
   _initial_size = num_committed_regions * reg_size_bytes;
+
+  size_t num_min_regions = min_byte_size / reg_size_bytes;
+  num_min_regions = MIN2(num_min_regions, _num_regions);
+  assert(num_min_regions <= _num_regions, "sanity");
+  _minimum_size = num_min_regions * reg_size_bytes;
+
   _committed = _initial_size;
 
   size_t heap_page_size   = UseLargePages ? (size_t)os::large_page_size() : (size_t)os::vm_page_size();
@@ -351,8 +357,11 @@
 
   _control_thread = new ShenandoahControlThread();
 
-  log_info(gc, init)("Initialize Shenandoah heap with initial size " SIZE_FORMAT "%s",
-                     byte_size_in_proper_unit(_initial_size), proper_unit_for_byte_size(_initial_size));
+  log_info(gc, init)("Initialize Shenandoah heap: " SIZE_FORMAT "%s initial, " SIZE_FORMAT "%s min, " SIZE_FORMAT "%s max",
+                     byte_size_in_proper_unit(_initial_size),  proper_unit_for_byte_size(_initial_size),
+                     byte_size_in_proper_unit(_minimum_size),  proper_unit_for_byte_size(_minimum_size),
+                     byte_size_in_proper_unit(max_capacity()), proper_unit_for_byte_size(max_capacity())
+  );
 
   log_info(gc, init)("Safepointing mechanism: %s",
                      SafepointMechanism::uses_thread_local_poll() ? "thread-local poll" :
@@ -622,6 +631,10 @@
   return _num_regions * ShenandoahHeapRegion::region_size_bytes();
 }
 
+size_t ShenandoahHeap::min_capacity() const {
+  return _minimum_size;
+}
+
 size_t ShenandoahHeap::initial_capacity() const {
   return _initial_size;
 }
@@ -635,12 +648,22 @@
 void ShenandoahHeap::op_uncommit(double shrink_before) {
   assert (ShenandoahUncommit, "should be enabled");
 
+  // Application allocates from the beginning of the heap, and GC allocates at
+  // the end of it. It is more efficient to uncommit from the end, so that applications
+  // could enjoy the near committed regions. GC allocations are much less frequent,
+  // and therefore can accept the committing costs.
+
   size_t count = 0;
-  for (size_t i = 0; i < num_regions(); i++) {
-    ShenandoahHeapRegion* r = get_region(i);
+  for (size_t i = num_regions(); i > 0; i--) { // care about size_t underflow
+    ShenandoahHeapRegion* r = get_region(i - 1);
     if (r->is_empty_committed() && (r->empty_time() < shrink_before)) {
       ShenandoahHeapLocker locker(lock());
       if (r->is_empty_committed()) {
+        // Do not uncommit below minimal capacity
+        if (committed() < min_capacity() + ShenandoahHeapRegion::region_size_bytes()) {
+          break;
+        }
+
         r->make_uncommitted();
         count++;
       }
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp	Tue Apr 09 21:20:15 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp	Tue Apr 09 21:20:16 2019 +0200
@@ -198,6 +198,7 @@
 //
 private:
            size_t _initial_size;
+           size_t _minimum_size;
   DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile size_t));
   volatile size_t _used;
   volatile size_t _committed;
@@ -216,6 +217,7 @@
   size_t bytes_allocated_since_gc_start();
   void reset_bytes_allocated_since_gc_start();
 
+  size_t min_capacity()     const;
   size_t max_capacity()     const;
   size_t initial_capacity() const;
   size_t capacity()         const;
--- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java	Tue Apr 09 21:20:15 2019 +0200
+++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java	Tue Apr 09 21:20:16 2019 +0200
@@ -31,6 +31,8 @@
  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC          -Xmx1g TestMemoryMXBeans   -1 1024
  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms1g   -Xmx1g TestMemoryMXBeans 1024 1024
  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms128m -Xmx1g TestMemoryMXBeans  128 1024
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms1g   -Xmx1g -XX:ShenandoahUncommitDelay=0 TestMemoryMXBeans 1024 1024
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms128m -Xmx1g -XX:ShenandoahUncommitDelay=0 TestMemoryMXBeans  128 1024
  */
 
 import java.lang.management.*;
@@ -46,6 +48,9 @@
         long initSize = 1L * Integer.parseInt(args[0]) * 1024 * 1024;
         long maxSize  = 1L * Integer.parseInt(args[1]) * 1024 * 1024;
 
+        // wait for GC to uncommit
+        Thread.sleep(1000);
+
         testMemoryBean(initSize, maxSize);
     }
 
@@ -65,7 +70,15 @@
             throw new IllegalStateException("Max heap size is wrong: " + heapMax + " vs " + maxSize);
         }
         if (initSize > 0 && maxSize > 0 && initSize != maxSize && heapCommitted == heapMax) {
-            throw new IllegalStateException("Init committed heap size is wrong: " + heapCommitted +
+            throw new IllegalStateException("Committed heap size is max: " + heapCommitted +
+                                            " (init: " + initSize + ", max: " + maxSize + ")");
+        }
+        if (initSize > 0 && maxSize > 0 && initSize == maxSize && heapCommitted != heapMax) {
+            throw new IllegalStateException("Committed heap size is not max: " + heapCommitted +
+                                            " (init: " + initSize + ", max: " + maxSize + ")");
+        }
+        if (initSize > 0 && heapCommitted < initSize) {
+            throw new IllegalStateException("Committed heap size is below min: " + heapCommitted +
                                             " (init: " + initSize + ", max: " + maxSize + ")");
         }
     }