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