8224185: ZGC: Introduce "High Usage" rule
authorpliden
Thu, 23 May 2019 08:09:29 +0200
changeset 55002 da9840e2f7da
parent 55001 7dd0430a6997
child 55003 bec1bb783c7e
8224185: ZGC: Introduce "High Usage" rule Reviewed-by: eosterlund
src/hotspot/share/gc/shared/gcCause.cpp
src/hotspot/share/gc/shared/gcCause.hpp
src/hotspot/share/gc/z/zDirector.cpp
src/hotspot/share/gc/z/zDirector.hpp
src/hotspot/share/gc/z/zDriver.cpp
test/hotspot/jtreg/gc/z/TestHighUsage.java
--- a/src/hotspot/share/gc/shared/gcCause.cpp	Wed May 22 21:49:31 2019 -0700
+++ b/src/hotspot/share/gc/shared/gcCause.cpp	Thu May 23 08:09:29 2019 +0200
@@ -141,6 +141,9 @@
     case _z_proactive:
       return "Proactive";
 
+    case _z_high_usage:
+      return "High Usage";
+
     case _last_gc_cause:
       return "ILLEGAL VALUE - last gc cause - ILLEGAL VALUE";
 
--- a/src/hotspot/share/gc/shared/gcCause.hpp	Wed May 22 21:49:31 2019 -0700
+++ b/src/hotspot/share/gc/shared/gcCause.hpp	Thu May 23 08:09:29 2019 +0200
@@ -91,6 +91,7 @@
     _z_allocation_rate,
     _z_allocation_stall,
     _z_proactive,
+    _z_high_usage,
 
     _last_gc_cause
   };
--- a/src/hotspot/share/gc/z/zDirector.cpp	Wed May 22 21:49:31 2019 -0700
+++ b/src/hotspot/share/gc/z/zDirector.cpp	Thu May 23 08:09:29 2019 +0200
@@ -181,6 +181,25 @@
   return time_until_gc <= 0;
 }
 
+bool ZDirector::rule_high_usage() const {
+  // Perform GC if the amount of free memory is 5% or less. This is a preventive
+  // meassure in the case where the application has a very low allocation rate,
+  // such that the allocation rate rule doesn't trigger, but the amount of free
+  // memory is still slowly but surely heading towards zero. In this situation,
+  // we start a GC cycle to avoid a potential allocation stall later.
+
+  // Calculate amount of free memory available to Java threads. Note that
+  // the heap reserve is not available to Java threads and is therefore not
+  // considered part of the free memory.
+  const size_t max_capacity = ZHeap::heap()->current_max_capacity();
+  const size_t max_reserve = ZHeap::heap()->max_reserve();
+  const size_t used = ZHeap::heap()->used();
+  const size_t free_with_reserve = max_capacity - used;
+  const size_t free = free_with_reserve - MIN2(free_with_reserve, max_reserve);
+
+  return percent_of(free, max_capacity) <= 5.0;
+}
+
 GCCause::Cause ZDirector::make_gc_decision() const {
   // Rule 0: Timer
   if (rule_timer()) {
@@ -202,6 +221,11 @@
     return GCCause::_z_proactive;
   }
 
+  // Rule 4: High usage
+  if (rule_high_usage()) {
+    return GCCause::_z_high_usage;
+  }
+
   // No GC
   return GCCause::_no_gc;
 }
--- a/src/hotspot/share/gc/z/zDirector.hpp	Wed May 22 21:49:31 2019 -0700
+++ b/src/hotspot/share/gc/z/zDirector.hpp	Thu May 23 08:09:29 2019 +0200
@@ -43,6 +43,7 @@
   bool rule_warmup() const;
   bool rule_allocation_rate() const;
   bool rule_proactive() const;
+  bool rule_high_usage() const;
   GCCause::Cause make_gc_decision() const;
 
 protected:
--- a/src/hotspot/share/gc/z/zDriver.cpp	Wed May 22 21:49:31 2019 -0700
+++ b/src/hotspot/share/gc/z/zDriver.cpp	Thu May 23 08:09:29 2019 +0200
@@ -234,6 +234,7 @@
   case GCCause::_z_allocation_rate:
   case GCCause::_z_allocation_stall:
   case GCCause::_z_proactive:
+  case GCCause::_z_high_usage:
   case GCCause::_metadata_GC_threshold:
     // Start asynchronous GC
     _gc_cycle_port.send_async(cause);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/z/TestHighUsage.java	Thu May 23 08:09:29 2019 +0200
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package gc.z;
+
+/*
+ * @test TestHighUsage
+ * @requires vm.gc.Z & !vm.graal.enabled
+ * @summary Test ZGC "High Usage" rule
+ * @library /test/lib
+ * @run main/othervm gc.z.TestHighUsage
+ */
+
+import java.util.LinkedList;
+import jdk.test.lib.process.ProcessTools;
+
+public class TestHighUsage {
+    static class Test {
+        private static final int K = 1024;
+        private static final int M = K * K;
+        private static final long startAt = 16 * M;
+        private static final long spikeAt = 4 * M;
+        private static volatile LinkedList<byte[]> keepAlive;
+        private static volatile Object dummy;
+
+        public static void main(String[] args) throws Exception {
+            System.out.println("Allocating live-set");
+
+            // Allocate live-set
+            keepAlive = new LinkedList<>();
+            while (Runtime.getRuntime().freeMemory() > startAt) {
+                while (Runtime.getRuntime().freeMemory() > startAt) {
+                    keepAlive.add(new byte[128 * K]);
+                }
+
+                // Compact live-set and let allocation rate settle down
+                System.gc();
+                Thread.sleep(2000);
+            }
+
+            System.out.println("Allocating garbage slowly");
+
+            // Allocate garbage slowly, such that the sampled allocation rate on
+            // average becomes zero MB/s for the last 1 second windows. If free
+            // memory goes below the spike limit we induce an allocation spike.
+            // The expected behavior is that the "High Usage" rule kicks in before
+            // the spike happens, avoiding an "Allocation Stall".
+            for (int i = 0; i < 300; i++) {
+                final long free = Runtime.getRuntime().freeMemory();
+                System.out.println("Free: " + (free / M) + "M");
+
+                if (free > spikeAt) {
+                    // Low allocation rate
+                    dummy = new byte[128 * K];
+                } else {
+                    // High allocation rate
+                    dummy = new byte[8 * M];
+                }
+
+                Thread.sleep(250);
+            }
+
+            System.out.println("Done");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ProcessTools.executeTestJvm(new String[]{ "-XX:+UnlockExperimentalVMOptions",
+                                                  "-XX:+UseZGC",
+                                                  "-XX:+UnlockDiagnosticVMOptions",
+                                                  "-XX:-ZProactive",
+                                                  "-Xms128M",
+                                                  "-Xmx128M",
+                                                  "-XX:ParallelGCThreads=1",
+                                                  "-XX:ConcGCThreads=1",
+                                                  "-Xlog:gc",
+                                                  Test.class.getName() })
+                    .shouldNotContain("Allocation Stall")
+                    .shouldContain("High Usage")
+                    .shouldHaveExitValue(0);
+    }
+}