Path: blob/master/test/hotspot/jtreg/vmTestbase/metaspace/shrink_grow/ShrinkGrowTest/ShrinkGrowTest.java
41155 views
/*1* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/222324/*25* @test26*27* @bug 821743228* @summary converted from VM Testbase metaspace/shrink_grow/ShrinkGrowTest.29*30* @requires vm.opt.final.ClassUnloading31* @library /vmTestbase /test/lib32* @run main/othervm33* -XX:MetaspaceSize=10m34* -XX:MaxMetaspaceSize=20m35* -Xlog:gc*:gc.log36* metaspace.shrink_grow.ShrinkGrowTest.ShrinkGrowTest37*/3839package metaspace.shrink_grow.ShrinkGrowTest;4041import java.lang.reflect.InvocationHandler;42import java.lang.reflect.Method;43import java.lang.reflect.Proxy;44import java.lang.management.ManagementFactory;45import java.lang.management.MemoryPoolMXBean;46import java.net.URL;47import java.net.URLClassLoader;48import java.util.HashMap;49import java.util.Map;5051/**52* This is the main test in the metaspace shrink/grow series.53*54* It tries to allocate all available metespace (loads new classes and keeps55* them in map), then checks that loading new classes causes OOM.56* After that it does cleanup loaded classes and then expect the new classes57* could be loaded again.58*59* <b>Note</b>: Don't forget to limit the metaspace size by giving60* -XX:MaxMetaspaceSize=100k vm option.61*/62public class ShrinkGrowTest {6364/**65* Dead classes storage.66*/67private final Map<String, ShrinkGrowTest.Foo> loadedClasses = new HashMap<>();6869private static int counter = 0;7071private String errorMessage = "not completed";7273// thread id to distinguish threads in output74private final String whoAmI;7576// the limit of classes to load expecting OOM77private final int maxClassesToLoad;7879public static void main(String[] args) {80String name = args.length > 0 ? args[0] : "singleTest" ;81new ShrinkGrowTest(name, 20000).run();82}8384/**85* @param name - thread id used in logging86* @param classesToLoad - the limit of classes to load expecting OOM87*/88public ShrinkGrowTest(String name, int classesToLoad) {89whoAmI = name;90maxClassesToLoad = classesToLoad;9192}9394/**95* Just outputs given message preceeded with the thread identifier96*97* @param message text to print out98*/99void log(String message) {100System.out.println("%" + whoAmI + "% " + message);101}102103void throwFault(String message) {104throw new TestFault("%" + whoAmI + "% " + message);105}106107void throwFault(String message, Throwable t) {108throw new TestFault("%" + whoAmI + "% " + message, t);109}110111/**112* Entry to the test.113* Just exits if passes or throws an Error if failed.114*/115public void run() {116if (System.getProperty("requiresCompressedClassSpace") != null &&117!isCompressedClassSpaceAvailable()) {118System.out.println("Not applicalbe, Compressed Class Space is required");119return;120}121122try {123log("Bootstrapping string concatenation for " + whoAmI );124go();125// The quest completed! Yahoo!126setErrorMessage(null);127log("passed");128} catch (TestFault failure) {129failure.printStackTrace(System.err);130setErrorMessage(failure.getMessage());131log("failed :" + errorMessage);132throw failure;133} catch (Throwable badThing) {134setErrorMessage(badThing.toString());135throw new TestFault(badThing);136}137}138139private void go() {140// step 1: eat all metaspace141log("eating metaspace");142runOutOfMetaspace(maxClassesToLoad);143144// step 2: try to load one more class145// it should be impossible146try {147eatALittleMemory();148throwFault("We haven't cleaned metaspace yet!");149} catch (OutOfMemoryError error) {150if (!isMetaspaceError(error)) {151throwFault("Hmm, we ran out metaspace. Metaspace error is still excpected here " + error, error);152}153}154155// step 3: clean up metaspace and try loading a class again.156log("washing hands before meal");157loadedClasses.clear();158System.gc();159try {160log("one more try to eat");161eatALittleMemory();162} catch (OutOfMemoryError error) {163throwFault("we already should be able to consume metaspace " + error, error);164}165}166167/**168* @return true if the test has successfully passed.169*/170public boolean isPassed() {171return errorMessage == null;172}173174/**175* @return message describing the reason of failure, or null if passes176*/177public String getErrorMessage() {178return errorMessage;179}180181/**182* Sets the message describing why test failed, or null if test passed183*/184void setErrorMessage(String msg) {185errorMessage = msg;186}187188/**189* Loads new classes until OOM.190* Checks that OOM is caused by metaspace and throws an Error if not.191*192* @param times - maximum limit of classes to load.193*/194private void runOutOfMetaspace(int times) {195try {196for (int i = 0; i < times; i++) {197eatALittleMemory();198}199} catch (OutOfMemoryError error) {200if (isMetaspaceError(error)) {201return;202}203throwFault("We ran out of another space, not metaspace: " + error, error);204}205throwFault("OOM hasn't happened after " + times + " iterations. Might be too much space?..");206}207208/**209* Imitates class loading.210* Each invocation of this method causes a new class loader object is created211* and a new class is loaded by this class loader.212* Method throws OOM when run out of memory.213*/214private void eatALittleMemory() {215try {216String jarUrl = "file:" + counter + ".jar";217counter++;218URL[] urls = new URL[]{new URL(jarUrl)};219URLClassLoader cl = new URLClassLoader(urls);220ShrinkGrowTest.Foo foo = (ShrinkGrowTest.Foo) Proxy.newProxyInstance(cl,221new Class[]{ShrinkGrowTest.Foo.class},222new ShrinkGrowTest.FooInvocationHandler(new ShrinkGrowTest.FooBar()));223loadedClasses.put(jarUrl, foo);224} catch (java.net.MalformedURLException badThing) {225// should never occur226throwFault("Unexpeted error: " + badThing, badThing);227}228229}230231/**232* Checks if given OOM is about metaspace233* @param error OOM234* @return true if message contains 'metaspace' word, false otherwise.235*/236boolean isMetaspaceError(OutOfMemoryError error) {237String message = error.getMessage();238return message != null && (message.contains("Metaspace") ||239message.contains("Compressed class space"));240}241242boolean isCompressedClassSpaceAvailable() {243for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {244if (pool.getName().equalsIgnoreCase("Compressed class space")) {245return true;246}247}248return false;249}250251/**252* Runtime exception signaling test failure.253*/254public static class TestFault extends RuntimeException {255public TestFault(String message) {256super(message);257}258public TestFault(Throwable t) {259super(t);260}261public TestFault(String message, Throwable t) {262super(message, t);263}264}265266public static interface Foo {267}268269public static class FooBar implements ShrinkGrowTest.Foo {270}271272class FooInvocationHandler implements InvocationHandler {273private final ShrinkGrowTest.Foo foo;274275FooInvocationHandler(ShrinkGrowTest.Foo foo) {276this.foo = foo;277}278279@Override280public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {281return method.invoke(foo, args);282}283}284}285286287