6e9f44c74c
During a minor GC, if memory allocation fails, start a full GC within the same VM operation in the same safepoint. This avoids a race where the GC locker can prevent the full GC from occurring, and a premature OoME. Co-authored-by: Axel Siebenborn <axel.siebenborn@sap.com> Reviewed-by: ehelin, sjohanss, phh
182 lines
6.2 KiB
Java
182 lines
6.2 KiB
Java
/*
|
|
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
|
* Copyright (c) 2017, SAP SE 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 TestJNIBlockFullGC
|
|
* @summary Check that in G1 a Full GC to reclaim space can not be blocked out by the GC locker.
|
|
* @key gc
|
|
* @requires vm.gc.G1
|
|
* @run main/othervm/native -Xmx64m -XX:+UseG1GC -Xlog:gc=info,gc+alloc=trace -XX:MaxGCPauseMillis=10 TestJNIBlockFullGC 10 10000 10000 10000 30000 10000 0.7
|
|
*/
|
|
|
|
import java.lang.ref.SoftReference;
|
|
|
|
public class TestJNIBlockFullGC {
|
|
|
|
static {
|
|
System.loadLibrary("TestJNIBlockFullGC");
|
|
}
|
|
|
|
public static volatile Object tmp;
|
|
|
|
public static volatile boolean hadError = false;
|
|
|
|
private static native int TestCriticalArray0(int[] x);
|
|
|
|
public static class Node {
|
|
public SoftReference<Node> next;
|
|
long payload1;
|
|
long payload2;
|
|
long payload3;
|
|
long payload4;
|
|
|
|
public Node(int load) {
|
|
payload1 = payload2 = payload3 = payload4 = load;
|
|
}
|
|
}
|
|
|
|
public static void warmUp(long warmupEndTime, int size) {
|
|
// First let the GC assume most of our objects will die.
|
|
Node[] roots = new Node[size];
|
|
|
|
while (System.currentTimeMillis() < warmupEndTime) {
|
|
int index = (int) (Math.random() * roots.length);
|
|
roots[index] = new Node(1);
|
|
}
|
|
|
|
// Make sure the young generation is empty.
|
|
for (int i = 0; i < roots.length; ++i) {
|
|
roots[i] = null;
|
|
}
|
|
}
|
|
|
|
public static void runTest(long endTime, int size, double alive) {
|
|
final int length = 10000;
|
|
int[] array1 = new int[length];
|
|
for (int x = 1; x < length; x++) {
|
|
array1[x] = x;
|
|
}
|
|
|
|
Node[] roots = new Node[size];
|
|
try {
|
|
int index = 0;
|
|
roots[0] = new Node(0);
|
|
|
|
while (!hadError && (System.currentTimeMillis() < endTime)) {
|
|
int test_val1 = TestCriticalArray0(array1);
|
|
|
|
if (Math.random() > alive) {
|
|
tmp = new Node(test_val1);
|
|
} else {
|
|
index = (int) (Math.random() * roots.length);
|
|
|
|
if (roots[index] != null) {
|
|
Node node = new Node(test_val1);
|
|
node.next = new SoftReference<Node>(roots[index]);
|
|
roots[index] = node;
|
|
} else {
|
|
roots[index] = new Node(test_val1);
|
|
}
|
|
}
|
|
}
|
|
} catch (OutOfMemoryError e) {
|
|
hadError = true;
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private static void joinThreads(Thread[] threads) throws Exception {
|
|
for (int i = 0; i < threads.length; i++) {
|
|
try {
|
|
if (threads[i] != null) {
|
|
threads[i].join();
|
|
}
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
if (args.length < 7){
|
|
System.out.println("Usage: java TestJNIBlockFullGC <warmupThreads> <warmup-time-in-millis> <warmup iterations> <threads> <time-in-millis> <iterations> <aliveFrac>");
|
|
System.exit(0);
|
|
}
|
|
|
|
int warmupThreads = Integer.parseInt(args[0]);
|
|
System.out.println("# Warmup Threads = " + warmupThreads);
|
|
|
|
int warmupDuration = Integer.parseInt(args[1]);
|
|
System.out.println("WarmUp Duration = " + warmupDuration);
|
|
int warmupIterations = Integer.parseInt(args[2]);
|
|
System.out.println("# Warmup Iterations = "+ warmupIterations);
|
|
|
|
int mainThreads = Integer.parseInt(args[3]);
|
|
System.out.println("# Main Threads = " + mainThreads);
|
|
int mainDuration = Integer.parseInt(args[4]);
|
|
System.out.println("Main Duration = " + mainDuration);
|
|
int mainIterations = Integer.parseInt(args[5]);
|
|
System.out.println("# Main Iterations = " + mainIterations);
|
|
|
|
double liveFrac = Double.parseDouble(args[6]);
|
|
System.out.println("Live Fraction = " + liveFrac);
|
|
|
|
Thread threads[] = new Thread[Math.max(warmupThreads, mainThreads)];
|
|
|
|
System.out.println("Start warm-up threads!");
|
|
long warmupStartTime = System.currentTimeMillis();
|
|
for (int i = 0; i < warmupThreads; i++) {
|
|
threads[i] = new Thread() {
|
|
public void run() {
|
|
warmUp(warmupStartTime + warmupDuration, warmupIterations);
|
|
};
|
|
};
|
|
threads[i].start();
|
|
}
|
|
|
|
joinThreads(threads);
|
|
|
|
System.gc();
|
|
System.out.println("Keep alive a lot");
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
for (int i = 0; i < mainThreads; i++) {
|
|
threads[i] = new Thread() {
|
|
public void run() {
|
|
runTest(startTime + mainDuration, mainIterations, liveFrac);
|
|
};
|
|
};
|
|
threads[i].start();
|
|
}
|
|
System.out.println("All threads started");
|
|
|
|
joinThreads(threads);
|
|
|
|
if (hadError) {
|
|
throw new RuntimeException("Experienced an OoME during execution.");
|
|
}
|
|
}
|
|
}
|