mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-17 17:07:53 +00:00
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:
parent
cfb0662ff6
commit
7445ebf02b
@ -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
|
||||
|
||||
#############################################################################
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user