1 /* 2 * Copyright (c) 2014, 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 package gc.g1.unloading.loading; 24 25 import gc.g1.unloading.ExecutionTask; 26 import gc.g1.unloading.bytecode.*; 27 //import gc.g1.unloading.check.*; 28 import gc.g1.unloading.check.Assertion; 29 import gc.g1.unloading.check.ClassAssertion; 30 import gc.g1.unloading.check.PhantomizedAssertion; 31 32 import gc.g1.unloading.check.FinalizedAssertion; 33 import gc.g1.unloading.check.PhantomizationServiceThread; 34 import gc.g1.unloading.check.cleanup.UnusedThreadKiller; 35 import gc.g1.unloading.classloaders.DoItYourselfClassLoader; 36 import gc.g1.unloading.classloaders.FinalizableClassloader; 37 import gc.g1.unloading.classloaders.JNIClassloader; 38 import gc.g1.unloading.classloaders.ReflectionClassloader; 39 import gc.g1.unloading.configuration.ClassloadingMethod; 40 import gc.g1.unloading.configuration.KeepRefMode; 41 import gc.g1.unloading.configuration.TestConfiguration; 42 import gc.g1.unloading.keepref.*; 43 import nsk.share.test.ExecutionController; 44 import sun.hotspot.WhiteBox; 45 import jdk.internal.misc.Unsafe; 46 47 import java.lang.ref.*; 48 import java.lang.reflect.Field; 49 import java.lang.reflect.InvocationTargetException; 50 import java.lang.reflect.Method; 51 import java.util.Collection; 52 import java.util.LinkedList; 53 import java.util.Random; 54 55 /** 56 * This helper performs dirty job: loads classes, instantiate objects, performs redefinition etc... 57 */ 58 public class ClassLoadingHelper { 59 60 private static final int NATIVE_VERBOSITY = 2; 61 62 private static final Object[] NO_CP_PATCHES = new Object[0]; 63 64 private static BytecodeFactory bf; 65 66 private ExecutionController executionController; 67 68 private PhantomizationServiceThread phantomizationServiceThread; 69 70 private Random random; 71 72 private TestConfiguration configuration; 73 74 /** 75 * Constructor that creates instance of helper. All arguments are self-explaining. 76 * @param executionController 77 * @param randomSeed 78 * @param testConfiguration 79 */ 80 public ClassLoadingHelper(ExecutionController executionController, 81 long randomSeed, TestConfiguration testConfiguration) { 82 random = new Random(randomSeed); 83 this.executionController = executionController; 84 this.configuration = testConfiguration; 85 86 phantomizationServiceThread = new PhantomizationServiceThread(executionController); 87 Thread thread = new Thread(phantomizationServiceThread); 88 thread.setDaemon(true); 89 thread.start(); 90 91 if (configuration.isInMemoryCompilation() && !configuration.isHumongousClass() && !(configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF)) { 92 bf = new BytecodeGeneratorFactory(random.nextLong()); 93 } else { 94 if (configuration.isHumongousClass()) { 95 bf = new BytecodeMutatorFactory(HumongousTemplateClass.class.getName()); 96 } else if (configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF) { 97 bf = new BytecodeMutatorFactory(ThreadTemplateClass.class.getName()); 98 } else { 99 bf = new BytecodeMutatorFactory(); 100 } 101 } 102 } 103 104 /** 105 * Load class that's supposed to live. Method returns collection of assertions to check it will live. 106 * @param className_ 107 * @return 108 */ 109 public Collection<Assertion> loadClassThatGonnaLive(String className_) { 110 Bytecode kit = bf.createBytecode(className_); 111 String className = kit.getClassName(); 112 byte[] bytecode = kit.getBytecode(); 113 Class<?> clazz = loadClass(className, bytecode); 114 Object object = instantiateObject(clazz); 115 Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object); 116 117 redefineIfNeeded(bytecode, clazz); 118 119 warmUpClassIfNeeded(object); 120 Assertion assertion; 121 // The JVM prepends the host class's package to the anonymous class name. 122 if (configuration.getClassloadingMethod() != ClassloadingMethod.ANONYMOUS_CLASSLOADER) { 123 assertion = new ClassAssertion(className, true); 124 } else { 125 assertion = new ClassAssertion("gc/g1/unloading/loading/" + className, true); 126 } 127 switch (configuration.getKeepRefMode()) { 128 case STRONG_REFERENCE: 129 assertion.keepLink(referenceToKeep); 130 break; 131 case SOFT_REFERENCE: 132 assertion.keepLink(new SoftReference<Object>(referenceToKeep)); 133 break; 134 case STATIC_FIELD: 135 RefHolder holder1 = new InStaticFieldHolder(); 136 assertion.keepLink(holder1.hold(referenceToKeep)); 137 break; 138 case STACK_LOCAL: 139 RefHolder holder2 = new InStackLocalHolder(); // UnusedThreadKiller 140 assertion.keepLink(holder2.hold(referenceToKeep)); 141 break; 142 case THREAD_FIELD: 143 RefHolder holder3 = new InThreadFieldHolder(); // UnusedThreadKiller 144 assertion.keepLink(holder3.hold(referenceToKeep)); 145 break; 146 case THREAD_ITSELF: 147 Thread objectThread = (Thread) object; 148 objectThread.setDaemon(true); 149 objectThread.start(); 150 assertion.keepLink(new UnusedThreadKiller(objectThread.getId())); // UnusedThreadKiller 151 break; 152 case STATIC_FIELD_OF_ROOT_CLASS: 153 RefHolder holder4 = new NullClassloaderHolder(random.nextLong()); 154 Object keep = holder4.hold(referenceToKeep); 155 if (keep != null) { 156 assertion.keepLink(keep); 157 } 158 break; 159 case JNI_GLOBAL_REF: 160 JNIGlobalRefHolder holder5 = new JNIGlobalRefHolder(); 161 assertion.keepLink(holder5.hold(referenceToKeep)); 162 break; 163 case JNI_LOCAL_REF: 164 JNILocalRefHolder holder6 = new JNILocalRefHolder(); 165 assertion.keepLink(holder6.hold(referenceToKeep)); 166 break; 167 } 168 169 Collection<Assertion> returnValue = new LinkedList<>(); 170 returnValue.add(assertion); 171 return returnValue; 172 } 173 174 /** 175 * Load class that's supposed to be unloaded. Method returns collection of assertions to check it will be unloaded. 176 * @param className_ 177 * @return 178 */ 179 public Collection<Assertion> loadClassThatGonnaDie(String className_) { 180 Collection<Assertion> returnValue = new LinkedList<>(); 181 Bytecode kit = bf.createBytecode(className_); 182 String className = kit.getClassName(); 183 byte[] bytecode = kit.getBytecode(); 184 Class<?> clazz = loadClass(className, bytecode); 185 FinalizableClassloader cl = null; 186 if (clazz.getClassLoader() instanceof FinalizableClassloader) { 187 cl = (FinalizableClassloader) clazz.getClassLoader(); 188 } 189 Object object = instantiateObject(clazz); 190 Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object); 191 192 redefineIfNeeded(bytecode, clazz); 193 194 warmUpClassIfNeeded(object); 195 Assertion assertion; 196 // The JVM prepends the host class's package to the anonymous class name. 197 if (configuration.getClassloadingMethod() != ClassloadingMethod.ANONYMOUS_CLASSLOADER) { 198 assertion = new ClassAssertion(className, false); 199 } else { 200 assertion = new ClassAssertion("gc/g1/unloading/loading/" + className, false); 201 } 202 switch (configuration.getReleaseRefMode()) { 203 case WEAK: 204 assertion.keepLink(new WeakReference<Object>(referenceToKeep)); 205 break; 206 case PHANTOM: 207 final ReferenceQueue queue = new ReferenceQueue<Object>(); 208 assertion.keepLink(new PhantomReference<Object>(referenceToKeep, queue)); 209 new Thread(new ReferenceCleaningThread(executionController, queue)).start(); 210 break; 211 } 212 returnValue.add(assertion); 213 214 if (cl != null) { 215 // Check that classloader will be finalized 216 FinalizedAssertion finalizedAssertion = new FinalizedAssertion(); 217 cl.setFinalizedAssertion(finalizedAssertion); 218 returnValue.add(finalizedAssertion); 219 220 // Check that classloader will be phantomized 221 PhantomizedAssertion phantomizedAssertion = new PhantomizedAssertion(); 222 PhantomReference phantomReference = new PhantomReference<Object>(cl, phantomizationServiceThread.getQueue()); 223 phantomizationServiceThread.add(phantomReference, phantomizedAssertion); 224 returnValue.add(phantomizedAssertion); 225 } 226 return returnValue; 227 } 228 229 private void redefineIfNeeded(byte[] bytecode, Class<?> clazz) { 230 if (configuration.isRedefineClasses()) { 231 BytecodePatcher.patch(bytecode); 232 makeRedefinition(NATIVE_VERBOSITY, clazz, bytecode); 233 234 // This will call class's method 235 instantiateObject(clazz); 236 } 237 } 238 239 private Class<?> loadClass(String className, byte[] bytecode) { 240 try { 241 switch (configuration.getClassloadingMethod()) { 242 case PLAIN: 243 DoItYourselfClassLoader loader1 = new DoItYourselfClassLoader(); 244 return loader1.defineClass(className, bytecode); 245 case REFLECTION: 246 return Class.forName(className, true, new ReflectionClassloader(bytecode, className)); 247 case JNI: 248 return JNIClassloader.loadThroughJNI(className, bytecode); 249 case ANONYMOUS_CLASSLOADER: 250 return getUnsafe().defineAnonymousClass(ClassLoadingHelper.class, bytecode, NO_CP_PATCHES); 251 } 252 return null; 253 } catch (ClassNotFoundException e) { 254 throw new RuntimeException("Test bug!", e); 255 } 256 } 257 258 private Object instantiateObject(Class<?> clazz) { 259 try { 260 Object object = clazz.newInstance(); 261 262 // Call method just for fun 263 for (Method m : clazz.getMethods()) { 264 if (m.getName().equals("main")) { 265 m.invoke(object); 266 } 267 } 268 return object; 269 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 270 throw new RuntimeException("Test bug!", e); 271 } 272 } 273 274 private void warmUpClassIfNeeded(Object object) { 275 if (configuration.getCompilationLevel() < 1 || configuration.getCompilationNumber() == 0) { 276 return; 277 } 278 Method m = null; 279 for (Method method : object.getClass().getMethods()) { 280 if (method.getName().equalsIgnoreCase("methodForCompilation")) { 281 m = method; 282 } 283 } 284 WhiteBox wb = WhiteBox.getWhiteBox(); 285 if (!wb.isMethodCompilable(m)) { 286 throw new RuntimeException("Test bug! Method occured to be not compilable. Requires investigation."); 287 } 288 289 for (int i = configuration.getCompilationNumber(); i >= 0 && executionController.continueExecution(); i--) { 290 if (!wb.isMethodCompilable(m, configuration.getCompilationLevel())) { 291 continue; 292 } 293 wb.enqueueMethodForCompilation(m, configuration.getCompilationLevel()); 294 while (!wb.isMethodCompiled(m) && executionController.continueExecution()) { 295 sleep(50); 296 try { 297 m.invoke(object, new Object()); 298 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 299 throw new RuntimeException("Something went wrong while compilation", e); 300 } 301 } 302 if (i > 0) { 303 wb.deoptimizeMethod(m); 304 } 305 } 306 } 307 308 native static int makeRedefinition0(int verbose, Class<?> redefClass, byte[] classBytes); 309 310 private static void makeRedefinition(int verbose, Class<?> redefClass, byte[] classBytes) { 311 new LibLoader().hashCode(); 312 if (makeRedefinition0(verbose, redefClass, classBytes) != 0) { 313 throw new RuntimeException("Test bug: native method \"makeRedefinition\" return nonzero"); 314 } 315 } 316 317 private static void sleep(long millis) { 318 try { 319 Thread.sleep(millis); 320 } catch (InterruptedException e) { 321 throw new RuntimeException("Got InterruptedException while sleeping.", e); 322 } 323 } 324 325 private static Unsafe getUnsafe() { 326 return Unsafe.getUnsafe(); 327 } 328 329 } 330 331 class ReferenceCleaningThread extends ExecutionTask { 332 333 private ReferenceQueue<?> queue; 334 335 public ReferenceCleaningThread(ExecutionController executionController, ReferenceQueue<?> queue) { 336 super(executionController); 337 this.queue = queue; 338 } 339 340 @Override 341 protected void task() throws Exception { 342 Reference<?> ref = queue.remove(100); 343 if (ref != null) { 344 ref.clear(); 345 return; 346 } 347 } 348 349 }