--- 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);
+ }
+}