1 /* 2 * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 /* 26 * @test 27 * 28 * @bug 8217432 29 * @summary converted from VM Testbase metaspace/shrink_grow/ShrinkGrowTest. 30 * 31 * @requires vm.opt.final.ClassUnloading 32 * @library /vmTestbase /test/lib 33 * @run driver jdk.test.lib.FileInstaller . . 34 * @run main/othervm 35 * -XX:MetaspaceSize=10m 36 * -XX:MaxMetaspaceSize=20m 37 * -Xlog:gc*:gc.log 38 * metaspace.shrink_grow.ShrinkGrowTest.ShrinkGrowTest 39 */ 40 41 package metaspace.shrink_grow.ShrinkGrowTest; 42 43 import java.lang.reflect.InvocationHandler; 44 import java.lang.reflect.Method; 45 import java.lang.reflect.Proxy; 46 import java.lang.management.ManagementFactory; 47 import java.lang.management.MemoryPoolMXBean; 48 import java.net.URL; 49 import java.net.URLClassLoader; 50 import java.util.HashMap; 51 import java.util.Map; 52 53 /** 54 * This is the main test in the metaspace shrink/grow series. 55 * 56 * It tries to allocate all available metespace (loads new classes and keeps 57 * them in map), then checks that loading new classes causes OOM. 58 * After that it does cleanup loaded classes and then expect the new classes 59 * could be loaded again. 60 * 61 * <b>Note</b>: Don't forget to limit the metaspace size by giving 62 * -XX:MaxMetaspaceSize=100k vm option. 63 */ 64 public class ShrinkGrowTest { 65 66 /** 67 * Dead classes storage. 68 */ 69 private final Map<String, ShrinkGrowTest.Foo> loadedClasses = new HashMap<>(); 70 71 private static int counter = 0; 72 73 private String errorMessage = "not completed"; 74 75 // thread id to distinguish threads in output 76 private final String whoAmI; 77 78 // the limit of classes to load expecting OOM 79 private final int maxClassesToLoad; 80 81 public static void main(String[] args) { 82 String name = args.length > 0 ? args[0] : "singleTest" ; 83 new ShrinkGrowTest(name, 20000).run(); 84 } 85 86 /** 87 * @param name - thread id used in logging 88 * @param classesToLoad - the limit of classes to load expecting OOM 89 */ 90 public ShrinkGrowTest(String name, int classesToLoad) { 91 whoAmI = name; 92 maxClassesToLoad = classesToLoad; 93 94 } 95 96 /** 97 * Just outputs given message preceeded with the thread identifier 98 * 99 * @param message text to print out 100 */ 101 void log(String message) { 102 System.out.println("%" + whoAmI + "% " + message); 103 } 104 105 void throwFault(String message) { 106 throw new TestFault("%" + whoAmI + "% " + message); 107 } 108 109 void throwFault(String message, Throwable t) { 110 throw new TestFault("%" + whoAmI + "% " + message, t); 111 } 112 113 /** 114 * Entry to the test. 115 * Just exits if passes or throws an Error if failed. 116 */ 117 public void run() { 118 if (System.getProperty("requiresCompressedClassSpace") != null && 119 !isCompressedClassSpaceAvailable()) { 120 System.out.println("Not applicalbe, Compressed Class Space is required"); 121 return; 122 } 123 124 try { 125 log("Bootstrapping string concatenation for " + whoAmI ); 126 go(); 127 // The quest completed! Yahoo! 128 setErrorMessage(null); 129 log("passed"); 130 } catch (TestFault failure) { 131 failure.printStackTrace(System.err); 132 setErrorMessage(failure.getMessage()); 133 log("failed :" + errorMessage); 134 throw failure; 135 } catch (Throwable badThing) { 136 setErrorMessage(badThing.toString()); 137 throw new TestFault(badThing); 138 } 139 } 140 141 private void go() { 142 // step 1: eat all metaspace 143 log("eating metaspace"); 144 runOutOfMetaspace(maxClassesToLoad); 145 146 // step 2: try to load one more class 147 // it should be impossible 148 try { 149 eatALittleMemory(); 150 throwFault("We haven't cleaned metaspace yet!"); 151 } catch (OutOfMemoryError error) { 152 if (!isMetaspaceError(error)) { 153 throwFault("Hmm, we ran out metaspace. Metaspace error is still excpected here " + error, error); 154 } 155 } 156 157 // step 3: clean up metaspace and try loading a class again. 158 log("washing hands before meal"); 159 loadedClasses.clear(); 160 System.gc(); 161 try { 162 log("one more try to eat"); 163 eatALittleMemory(); 164 } catch (OutOfMemoryError error) { 165 throwFault("we already should be able to consume metaspace " + error, error); 166 } 167 } 168 169 /** 170 * @return true if the test has successfully passed. 171 */ 172 public boolean isPassed() { 173 return errorMessage == null; 174 } 175 176 /** 177 * @return message describing the reason of failure, or null if passes 178 */ 179 public String getErrorMessage() { 180 return errorMessage; 181 } 182 183 /** 184 * Sets the message describing why test failed, or null if test passed 185 */ 186 void setErrorMessage(String msg) { 187 errorMessage = msg; 188 } 189 190 /** 191 * Loads new classes until OOM. 192 * Checks that OOM is caused by metaspace and throws an Error if not. 193 * 194 * @param times - maximum limit of classes to load. 195 */ 196 private void runOutOfMetaspace(int times) { 197 try { 198 for (int i = 0; i < times; i++) { 199 eatALittleMemory(); 200 } 201 } catch (OutOfMemoryError error) { 202 if (isMetaspaceError(error)) { 203 return; 204 } 205 throwFault("We ran out of another space, not metaspace: " + error, error); 206 } 207 throwFault("OOM hasn't happened after " + times + " iterations. Might be too much space?.."); 208 } 209 210 /** 211 * Imitates class loading. 212 * Each invocation of this method causes a new class loader object is created 213 * and a new class is loaded by this class loader. 214 * Method throws OOM when run out of memory. 215 */ 216 private void eatALittleMemory() { 217 try { 218 String jarUrl = "file:" + counter + ".jar"; 219 counter++; 220 URL[] urls = new URL[]{new URL(jarUrl)}; 221 URLClassLoader cl = new URLClassLoader(urls); 222 ShrinkGrowTest.Foo foo = (ShrinkGrowTest.Foo) Proxy.newProxyInstance(cl, 223 new Class[]{ShrinkGrowTest.Foo.class}, 224 new ShrinkGrowTest.FooInvocationHandler(new ShrinkGrowTest.FooBar())); 225 loadedClasses.put(jarUrl, foo); 226 } catch (java.net.MalformedURLException badThing) { 227 // should never occur 228 throwFault("Unexpeted error: " + badThing, badThing); 229 } 230 231 } 232 233 /** 234 * Checks if given OOM is about metaspace 235 * @param error OOM 236 * @return true if message contains 'metaspace' word, false otherwise. 237 */ 238 boolean isMetaspaceError(OutOfMemoryError error) { 239 String message = error.getMessage(); 240 return message != null && (message.contains("Metaspace") || 241 message.contains("Compressed class space")); 242 } 243 244 boolean isCompressedClassSpaceAvailable() { 245 for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) { 246 if (pool.getName().equalsIgnoreCase("Compressed class space")) { 247 return true; 248 } 249 } 250 return false; 251 } 252 253 /** 254 * Runtime exception signaling test failure. 255 */ 256 public static class TestFault extends RuntimeException { 257 public TestFault(String message) { 258 super(message); 259 } 260 public TestFault(Throwable t) { 261 super(t); 262 } 263 public TestFault(String message, Throwable t) { 264 super(message, t); 265 } 266 } 267 268 public static interface Foo { 269 } 270 271 public static class FooBar implements ShrinkGrowTest.Foo { 272 } 273 274 class FooInvocationHandler implements InvocationHandler { 275 private final ShrinkGrowTest.Foo foo; 276 277 FooInvocationHandler(ShrinkGrowTest.Foo foo) { 278 this.foo = foo; 279 } 280 281 @Override 282 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 283 return method.invoke(foo, args); 284 } 285 } 286 }