# HG changeset patch # User ehelin # Date 1492506069 -7200 # Node ID 2dd13ff72976fb0b95f8cd092b35ee318067ee25 # Parent 4cca6758a9ee850cb4c7b4f07c9e6d6a7eb6f117 8177968: Add GC stress test TestGCLocker Reviewed-by: pliden, lmesnik diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/make/test/JtregNative.gmk --- a/hotspot/make/test/JtregNative.gmk Wed Apr 05 14:21:58 2017 +0200 +++ b/hotspot/make/test/JtregNative.gmk Tue Apr 18 11:01:09 2017 +0200 @@ -43,6 +43,7 @@ # Add more directories here when needed. BUILD_HOTSPOT_JTREG_NATIVE_SRC += \ + $(HOTSPOT_TOPDIR)/test/gc/stress/gclocker \ $(HOTSPOT_TOPDIR)/test/native_sanity \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8025979 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \ diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/TestGCLocker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/TestGCLocker.java Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +// Stress the GC locker by calling GetPrimitiveArrayCritical while +// concurrently filling up old gen. + +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryUsage; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +final class ThreadUtils { + public static void sleep(long durationMS) { + try { + Thread.sleep(durationMS); + } catch (Exception e) { + } + } +} + +class Filler { + private static final int SIZE = 250000; + + private int[] i1 = new int[SIZE]; + private int[] i2 = new int[SIZE]; + private short[] s1 = new short[SIZE]; + private short[] s2 = new short[SIZE]; + + private Map map = new HashMap<>(); + + public Filler() { + for (int i = 0; i < 10000; i++) { + map.put(new Object(), new Object()); + } + } +} + +class Exitable { + private volatile boolean shouldExit = false; + + protected boolean shouldExit() { + return shouldExit; + } + + public void exit() { + shouldExit = true; + } +} + +class MemoryWatcher { + private MemoryPoolMXBean bean; + private final int thresholdPromille = 750; + private final int criticalThresholdPromille = 800; + private final int minGCWaitMS = 1000; + private final int minFreeWaitElapsedMS = 30000; + private final int minFreeCriticalWaitMS = 500; + + private int lastUsage = 0; + private long lastGCDetected = System.currentTimeMillis(); + private long lastFree = System.currentTimeMillis(); + + public MemoryWatcher(String mxBeanName) { + List memoryBeans = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean bean : memoryBeans) { + if (bean.getName().equals(mxBeanName)) { + this.bean = bean; + break; + } + } + } + + private int getMemoryUsage() { + if (bean == null) { + Runtime r = Runtime.getRuntime(); + float free = (float) r.freeMemory() / r.maxMemory(); + return Math.round((1 - free) * 1000); + } else { + MemoryUsage usage = bean.getUsage(); + float used = (float) usage.getUsed() / usage.getCommitted(); + return Math.round(used * 1000); + } + } + + public synchronized boolean shouldFreeUpSpace() { + int usage = getMemoryUsage(); + long now = System.currentTimeMillis(); + + boolean detectedGC = false; + if (usage < lastUsage) { + lastGCDetected = now; + detectedGC = true; + } + + lastUsage = usage; + + long elapsed = now - lastFree; + long timeSinceLastGC = now - lastGCDetected; + + if (usage > criticalThresholdPromille && elapsed > minFreeCriticalWaitMS) { + lastFree = now; + return true; + } else if (usage > thresholdPromille && !detectedGC) { + if (elapsed > minFreeWaitElapsedMS || timeSinceLastGC > minGCWaitMS) { + lastFree = now; + return true; + } + } + + return false; + } +} + +class MemoryUser extends Exitable implements Runnable { + private final Queue cache = new ArrayDeque(); + private final MemoryWatcher watcher; + + private void load() { + if (watcher.shouldFreeUpSpace()) { + int toRemove = cache.size() / 5; + for (int i = 0; i < toRemove; i++) { + cache.remove(); + } + } + cache.add(new Filler()); + } + + public MemoryUser(String mxBeanName) { + watcher = new MemoryWatcher(mxBeanName); + } + + @Override + public void run() { + for (int i = 0; i < 200; i++) { + load(); + } + + while (!shouldExit()) { + load(); + } + } +} + +class GCLockerStresser extends Exitable implements Runnable { + static native void fillWithRandomValues(byte[] array); + + @Override + public void run() { + byte[] array = new byte[1024 * 1024]; + while (!shouldExit()) { + fillWithRandomValues(array); + } + } +} + +public class TestGCLocker { + private static Exitable startGCLockerStresser(String name) { + GCLockerStresser task = new GCLockerStresser(); + + Thread thread = new Thread(task); + thread.setName(name); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + + return task; + } + + private static Exitable startMemoryUser(String mxBeanName) { + MemoryUser task = new MemoryUser(mxBeanName); + + Thread thread = new Thread(task); + thread.setName("Memory User"); + thread.start(); + + return task; + } + + public static void main(String[] args) { + System.loadLibrary("TestGCLocker"); + + long durationMinutes = args.length > 0 ? Long.parseLong(args[0]) : 5; + String mxBeanName = args.length > 1 ? args[1] : null; + + long startMS = System.currentTimeMillis(); + + Exitable stresser1 = startGCLockerStresser("GCLockerStresser1"); + Exitable stresser2 = startGCLockerStresser("GCLockerStresser2"); + Exitable memoryUser = startMemoryUser(mxBeanName); + + long durationMS = durationMinutes * 60 * 1000; + while ((System.currentTimeMillis() - startMS) < durationMS) { + ThreadUtils.sleep(10 * 1010); + } + + stresser1.exit(); + stresser2.exit(); + memoryUser.exit(); + } +} diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/TestGCLockerWithCMS.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/TestGCLockerWithCMS.java Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +/* + * @test TestGCLockerWithCMS + * @key gc + * @requires vm.gc.ConcMarkSweep + * @summary Stress CMS' GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen. + * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xms1500m -Xmx1500m -XX:+UseConcMarkSweepGC TestGCLockerWithCMS + */ +public class TestGCLockerWithCMS { + public static void main(String[] args) { + String[] testArgs = {"2", "CMS Old Gen"}; + TestGCLocker.main(testArgs); + } +} diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/TestGCLockerWithG1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/TestGCLockerWithG1.java Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +/* + * @test TestGCLockerWithG1 + * @key gc + * @requires vm.gc.G1 + * @summary Stress G1's GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen. + * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xms1500m -Xmx1500m -XX:+UseG1GC TestGCLockerWithG1 + */ +public class TestGCLockerWithG1 { + public static void main(String[] args) { + String[] testArgs = {"2", "G1 Old Gen"}; + TestGCLocker.main(testArgs); + } +} diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/TestGCLockerWithParallel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/TestGCLockerWithParallel.java Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +/* + * @test TestGCLockerWithParallel + * @key gc + * @requires vm.gc.Parallel + * @summary Stress Parallel's GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen. + * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xms1500m -Xmx1500m -XX:+UseParallelGC TestGCLockerWithParallel + */ +public class TestGCLockerWithParallel { + public static void main(String[] args) { + String[] testArgs = {"2", "PS Old Gen"}; + TestGCLocker.main(testArgs); + } +} diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/TestGCLockerWithSerial.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/TestGCLockerWithSerial.java Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +/* + * @test TestGCLockerWithSerial + * @key gc + * @requires vm.gc.Serial + * @summary Stress Serial's GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen. + * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xmx1500m -Xmx1500m -XX:+UseSerialGC TestGCLockerWithSerial + */ +public class TestGCLockerWithSerial { + public static void main(String[] args) { + String[] testArgs = {"2", "Tenured Gen"}; + TestGCLocker.main(testArgs); + } +} diff -r 4cca6758a9ee -r 2dd13ff72976 hotspot/test/gc/stress/gclocker/libTestGCLocker.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/stress/gclocker/libTestGCLocker.c Tue Apr 18 11:01:09 2017 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 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. + */ + +#include + +JNIEXPORT void JNICALL +Java_GCLockerStresser_fillWithRandomValues(JNIEnv* env, jclass clz, jbyteArray arr) { + jsize size = (*env)->GetArrayLength(env, arr); + jbyte* p = (*env)->GetPrimitiveArrayCritical(env, arr, NULL); + jsize i; + for (i = 0; i < size; i++) { + p[i] = i % 128; + } + (*env)->ReleasePrimitiveArrayCritical(env, arr, p, 0); +}