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