8208697: vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TestDescription.java fails with OutOfMemoryError: Metaspace

Remove timeoutHandler class and let Stresser handle timeout, remove 30 threads filling metaspace, and remove related unused files.

Reviewed-by: lfoltan, mseledtsov
This commit is contained in:
Coleen Phillimore 2018-09-10 16:33:55 -04:00
parent cfb0662ff6
commit 7445ebf02b
11 changed files with 25 additions and 598 deletions

View File

@ -214,11 +214,4 @@ vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_b/TestDescription.java
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TestDescription.java 8208697 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TestDescription.java 8208697 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TestDescription.java 8208697 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TestDescription.java 8208697 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TestDescription.java 8208697 generic-all
vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TestDescription.java 8208697 generic-all
#############################################################################

View File

@ -137,10 +137,10 @@ public class PerformChecksHelper {
}
}
} catch (OutOfMemoryError e) {
if (e.getMessage().trim().toLowerCase().contains("metadata")) {
System.out.println("Got OOME in metaspace in PerformChecksHelper.callMethods(Class clazz). " +
"This happened because reflection generates a too many accessors. " +
"There is nothing we can do with it, so we are just suppressing.");
if (e.getMessage().trim().toLowerCase().contains("metaspace")) {
// avoid string concatenation, which may create more classes.
System.out.println("Got OOME in metaspace in PerformChecksHelper.callMethods(Class clazz). ");
System.out.println("This is possible with -triggerUnloadingByFillingMetaspace");
} else {
throw e;
}

View File

@ -39,15 +39,13 @@ import metaspace.stressHierarchy.common.generateHierarchy.TreeDescriptor;
import nsk.share.test.ExecutionController;
import nsk.share.test.Stresser;
import nsk.share.test.TestBase;
import nsk.share.test.timeoutwatchdog.TimeoutHandler;
import nsk.share.test.timeoutwatchdog.TimeoutWatchdog;
/**
* Superclass for StressHierarchy* tests. It provides util methods to create and load
* classes hierarchy and perform checks.
*/
abstract public class StressHierarchyBaseClass extends TestBase implements TimeoutHandler {
abstract public class StressHierarchyBaseClass extends TestBase {
protected static String[] args;
@ -101,7 +99,6 @@ abstract public class StressHierarchyBaseClass extends TestBase implements Timeo
long startTimeStamp = System.currentTimeMillis();
ExecutionController stresser = new Stresser(args);
stresser.start(1);
TimeoutWatchdog.watch(stresser, this);
TreeDescriptor treeDescriptor = GenerateHierarchyHelper.generateHierarchy(treeDepth, minLevelSize, maxLevelSize, hierarchyType);
Tree tree = buildTree(treeDescriptor);
System.out.println("Generating took " + ((System.currentTimeMillis() - startTimeStamp)/1000) +" sec");
@ -128,12 +125,6 @@ abstract public class StressHierarchyBaseClass extends TestBase implements Timeo
}
}
@Override
public void handleTimeout() {
System.out.println("Shutting down vm because of time expired.");
System.exit(95);
}
abstract protected void runTestLogic(Tree tree, ExecutionController stresser) throws Throwable;
private Tree buildTree(TreeDescriptor treeDescriptor) throws MalformedURLException,

View File

@ -37,7 +37,7 @@
* @comment generate and compile metaspace.stressHierarchy.common.HumongousClass
* @run driver metaspace.stressHierarchy.common.GenClassesBuilder
* @run main/othervm
* -XX:MaxMetaspaceSize=450m
* -XX:MaxMetaspaceSize=250m
* -Xss10m
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions

View File

@ -119,8 +119,6 @@ public class GeneratingClassLoader extends ClassLoader {
return bytecode;
} catch (UnsupportedEncodingException e) {
throw new TestBug(e);
} catch (IOException e) {
throw new TestBug(e);
}
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (c) 2013, 2018, 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 nsk.share.test.timeoutwatchdog;
/**
* TimeoutHandler - interface to define reaction on timeout.
* @see TimeoutWatchdoc
*/
public interface TimeoutHandler {
/**
* Invoked when watchdog detects timeout. Subclasses must implement this method to define how timeout should be handled.
*/
void handleTimeout();
}

View File

@ -1,73 +0,0 @@
/*
* Copyright (c) 2013, 2018, 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 nsk.share.test.timeoutwatchdog;
import nsk.share.test.ExecutionController;
/**
* This class watches for ExecutionControler and notifies TimeoutHander in case of timeout.
*/
public class TimeoutWatchdog implements Runnable {
private ExecutionController executionController;
private TimeoutHandler handler;
private static long CHECK_PERIOD = 1000; // In milliseconds
private TimeoutWatchdog(ExecutionController executionController, TimeoutHandler handler) {
this.executionController = executionController;
this.handler = handler;
}
/**
* Start watching for timeout.
* This method runs a new daemon thread that checks periodically if the observable test is still running.
* If timeout is detected <code>handler.handleTimeout()</code> will be called. If the test finishes normally the daemon
* thread will silently die.
* @param executionController - executionController used to monitor time left
* @param handler - handler on which handleTimeout() will be called
*/
public static void watch(ExecutionController executionController, TimeoutHandler handler) {
Thread thread = new Thread(new TimeoutWatchdog(executionController, handler));
thread.setName("TimeoutWatchdog_thread");
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(CHECK_PERIOD);
if (!executionController.continueExecution()) {
System.out.println("Time expired. TimeoutWatchdog is calling TimeoutHandler.handleTimeout.");
handler.handleTimeout();
}
}
} catch (InterruptedException e) {
throw new RuntimeException("Somebody dared to interrupt TimeoutWatchdog thread.");
}
}
}

View File

@ -1,69 +0,0 @@
/*
* Copyright (c) 2013, 2018, 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 vm.share.gc;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import nsk.share.TestFailure;
import nsk.share.test.ExecutionController;
public class TriggerUnloadingByFillingHeap implements TriggerUnloadingHelper {
public void triggerUnloading(ExecutionController stresser) {
List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
if (jvmArgs.contains("-XX:+ExplicitGCInvokesConcurrent")) {
throw new TestFailure("Test bug! Found -XX:+ExplicitGCInvokesConcurrent in jvm args. TriggerUnloadingByFillingHeap.triggerUnloading will not work!.");
}
System.out.println("collections invoked: " + provokeGC(stresser));
System.out.println("collections invoked: " + provokeGC(stresser));
System.out.println("collections invoked: " + provokeGC(stresser));
}
private static long getGCCounter() {
return ManagementFactory.getGarbageCollectorMXBeans().get(1).getCollectionCount();
}
private static Random random = new Random();
public static byte[] garbage; //make it reference public to avoid compiler optimizations
private static long provokeGC(ExecutionController stresser) {
long initCounter = getGCCounter();
ArrayList<byte[]> list = new ArrayList<byte[]>();
while (getGCCounter() == initCounter && stresser.continueExecution()) {
list.add(new byte[1024]);
garbage = new byte[1024];
if (random.nextInt(10) % 10 < 3 && !list.isEmpty()) {
list.remove(0);
}
System.gc();
}
return getGCCounter() - initCounter;
}
}

View File

@ -22,76 +22,38 @@
*/
package vm.share.gc;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import nsk.share.test.ExecutionController;
import metaspace.stressHierarchy.common.exceptions.GotWrongOOMEException;
import nsk.share.gc.gp.classload.GeneratedClassProducer;
import nsk.share.test.ExecutionController;
public class TriggerUnloadingByFillingMetaspace implements
TriggerUnloadingHelper {
private static final int NUMBER_OF_THREADS = 30;
private volatile boolean gotOOME = false;
private ExecutionController stresser;
private final ThreadLocal<GeneratedClassProducer> generatedClassProducer =
new ThreadLocal<GeneratedClassProducer>() {
@Override
protected GeneratedClassProducer initialValue() {
return new GeneratedClassProducer("metaspace.stressHierarchy.common.HumongousClass");
}
};
private static class FillMetaspace {
private volatile boolean gotOOME = false;
private ExecutionController stresser;
private final ThreadLocal<GeneratedClassProducer> generatedClassProducer =
new ThreadLocal<GeneratedClassProducer>() {
@Override
protected GeneratedClassProducer initialValue() {
return new GeneratedClassProducer("metaspace.stressHierarchy.common.HumongousClass");
}
};
public FillMetaspace(ExecutionController stresser) { this.stresser = stresser; }
private class FillMetaspaceTask implements Callable<Object> {
@Override
public Object call() throws Exception {
while (stresser.continueExecution() && ! gotOOME) {
try {
generatedClassProducer.get().create(-100500); //argument is not used.
} catch (OutOfMemoryError oome) {
if (!isInMetaspace(oome)) {
throw new GotWrongOOMEException("Got OOME in heap while gaining OOME in metaspace. Test result can't be valid.");
}
gotOOME = true;
}
}
return null;
}
}
}
private static boolean isInMetaspace(OutOfMemoryError error) {
return error.getMessage().trim().toLowerCase().contains("metadata");
private static boolean isInMetaspace(Throwable error) {
return (error.getMessage().trim().toLowerCase().contains("metaspace"));
}
@Override
public void triggerUnloading(ExecutionController stresser) {
try {
FillMetaspace fillMetaspace = new FillMetaspace(stresser);
ArrayList<Callable<Object>> tasks = new ArrayList<Callable<Object>>(NUMBER_OF_THREADS);
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
tasks.add(fillMetaspace.new FillMetaspaceTask());
}
ExecutorService executorService = Executors.newCachedThreadPool();
while (stresser.continueExecution() && !gotOOME) {
try {
executorService.invokeAll(tasks);
} catch (InterruptedException e) {
System.out.println("Process of gaining OOME in metaspace was interrupted.");
e.printStackTrace();
generatedClassProducer.get().create(-100500); //argument is not used.
} catch (Throwable oome) {
if (!isInMetaspace(oome)) {
throw new GotWrongOOMEException("Got OOME in heap while triggering OOME in metaspace. Test result can't be valid.");
}
gotOOME = true;
}
} catch (OutOfMemoryError e) {
if (!isInMetaspace(e)) {
throw new GotWrongOOMEException("Got OOME in heap while gaining OOME in metaspace. Test result can't be valid.");
}
return;
}
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright (c) 2012, 2018, 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 vm.share.vmstresser;
public class CompileAndDeoptimize implements Runnable {
public static int v = 0;
private abstract static class A {
public abstract void incv();
}
private static class B extends A {
public void incv() {
v++;
}
}
public static class C extends A {
public void incv() {
v += (new int[1][1][1][1][1][1][1][1]).length;
}
}
private volatile boolean done = false;
public volatile A a = new B();
private void incv() {
a.incv();
}
private void inc() {
while ( ! done ) {
incv();
}
//while ( ! done ) {
// incv();
//}
//while ( ! done ) {
// incv();
//}
}
public void run() {
try {
Thread t = new Thread(new Runnable() { @Override public void run() { inc(); } });
t.start();
Thread.sleep(100);
a = (A) CompileAndDeoptimize.class.getClassLoader().loadClass(B.class.getName().replaceAll("B$", "C")).getConstructors()[0].newInstance(new Object[0]);
//Thread.sleep(1000);
//done = true;
//t.join();
} catch ( Throwable t ) {
t.printStackTrace();
}
}
}

View File

@ -1,260 +0,0 @@
/*
* Copyright (c) 2013, 2018, 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 vm.share.vmstresser;
import java.util.*;
import java.util.concurrent.locks.*;
import nsk.share.*;
import nsk.share.classload.*;
import nsk.share.test.*;
/**
* Stresser that load classes until OOME, then unload some of them and continue loading.
*/
public class MetaspaceStresser extends Thread {
/**
* Capacity of class containers.
* This amount of classes will be unloaded on reset call.
*/
public static final int DEFAULT_BUCKET_SIZE = 4000;
public static final int DEFAULT_PAUSE_TIME = 0;
/*
* Loaded classes stored in ClassContainer instances.
* Such instances organized in array-based stack as it is
* one of the simplest way to minimize possibility
* to get OOME and guarntee that after replacing
* reference to class container by null there will be
* no cached refereces and container will be reclaimed by
* GC and classes will become unloadable.
*/
// Maximum available amount of arrays with class containers.
private static final int CONTAINERS_ARRAY_LENGTH = 1000;
// Maximum length array with class containers.
private static final int CONTAINER_ARRAYS_COUNT = 100;
private ClassContainersStack containersStack = new ClassContainersStack(CONTAINER_ARRAYS_COUNT * CONTAINERS_ARRAY_LENGTH,
CONTAINERS_ARRAY_LENGTH);
private ClassContainer newContainer = null;
private ExecutionController controller = null;
private int bucketSize = DEFAULT_BUCKET_SIZE;
private int pauseTime = DEFAULT_PAUSE_TIME;
private ReentrantLock lock = new ReentrantLock();
/**
* Construct MetaspaceStrresser with default bucket size
* and pause time.
* @param c controller to control execution time.
*/
public MetaspaceStresser(ExecutionController c) {
controller = c;
}
/**
* Construct MetaspaceStrresser with custom bucket size
* and pause time.
* @param c controller to control execution time.
* @param bucketSize classes to be unloaded on reset.
* @param pauseTime pause after reset.
*/
public MetaspaceStresser(ExecutionController c, int bucketSize, int pauseTime) {
this(c);
this.bucketSize = bucketSize;
this.pauseTime = pauseTime;
}
/**
* Fill Metaspace with classes.
* Classes will be loaded until OOME, then some of them will be unloaded.
*/
public synchronized void prepare() {
while (controller.continueExecution()) {
try {
fillContainerStack();
} catch (OutOfMemoryError oome) {
unloadLastClassBucket();
return;
} catch (ClassNotFoundException cnfe) {
throw new TestBug("Unexpected exception in stresser.", cnfe);
}
}
}
/**
* Load new class to container, fill containerStack.
* Classes will be loaded until OOME
* @throws ClassNotFoundException
*/
private void fillContainerStack() throws ClassNotFoundException {
newContainer = new ClassContainer();
while (newContainer.size() < bucketSize && controller.continueExecution()) {
newContainer.loadClass();
}
containersStack.push(newContainer);
newContainer = null;
}
/**
* Run stresser.
* Stresser will load classes until OOME, then bucketSize classes
* will be unloaded and stresser will wait pauseTime millisiconds
* before continuing class loading.
*/
public void run() {
try {
while (controller.continueExecution()) {
try {
fillContainerStack();
} catch (OutOfMemoryError oome) {
unloadLastClassBucket();
try {
Thread.sleep(pauseTime);
} catch (InterruptedException ie) {
}
}
}
} catch (Throwable e) {
throw new TestBug("Unexpected exception in stresser.", e);
} finally {
containersStack.free();
}
}
/**
* Unload most recently loaded bucket of classes.
*/
public void unloadLastClassBucket() {
while (controller.continueExecution()) {
try {
containersStack.pop();
System.gc();
break;
} catch (OutOfMemoryError oome) {
oome.printStackTrace();
continue;
}
}
}
/**
* Array-based stack for ClassContainer's.
*/
private class ClassContainersStack {
private int arrayLength = 0;
private int arraysCount = 0;
private int arrayIndex = 0;
private int elemIndex = 0;
private ClassContainer data[][];
/**
* Create ClassContainersStack that will be able
* to store size classes in arrays of segmentSize length.
*/
public ClassContainersStack(int size, int segementSize) {
arrayLength = segementSize;
arraysCount = size / arrayLength;
data = new ClassContainer[arraysCount][];
data[0] = new ClassContainer[arrayLength];
}
/**
* Push ClassContainer c into stack.
*/
public synchronized void push(ClassContainer c) {
data[arrayIndex][elemIndex] = c;
elemIndex++;
if (elemIndex == arrayLength) {
if (arrayIndex == arraysCount) {
throw new TestBug("ClassContainersStack ran out of available slots");
}
data[arrayIndex + 1] = new ClassContainer[arrayLength];
arrayIndex++;
elemIndex = 0;
}
}
/**
* Remove reference to top ClassContainer.
*/
public synchronized void pop() {
data[arrayIndex][elemIndex] = null;
if (elemIndex > 0) {
elemIndex--;
} else if (arrayIndex > 0) {
data[arrayIndex] = null;
arrayIndex--;
elemIndex = arrayLength - 1;
}
}
/**
* Remove all stored ClassContainers.
*/
public synchronized void free() {
data = null;
System.gc();
data = new ClassContainer[arraysCount][];
data[0] = new ClassContainer[arrayLength];
arrayIndex = 0;
elemIndex = 0;
}
}
/// Variable used to create uniqe name for generated classes.
private static long lastClass = 0;
/**
* Class container consists of classes and their ClassLoader, so
* if there will be no references to container and classes inside it then
* it could be easely collected by GC.
*/
private class ClassContainer {
private List<Class> classes = new LinkedList<Class>();
private GeneratingClassLoader loader = new GeneratingClassLoader();
private String prefix = loader.getPrefix();
private int length = loader.getNameLength();
public void loadClass() throws ClassNotFoundException {
String newName = prefix + "c" + lastClass;
lastClass++;
while (newName.length() < length) {
newName = newName + "c";
}
classes.add(loader.loadClass(newName));
}
public int size() {
return classes.size();
}
}
}