Path: blob/master/test/hotspot/jtreg/vmTestbase/gc/g1/unloading/loading/ClassLoadingHelper.java
41161 views
/*1* Copyright (c) 2014, 2021, 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*/22package gc.g1.unloading.loading;2324import gc.g1.unloading.ExecutionTask;25import gc.g1.unloading.bytecode.*;26import gc.g1.unloading.check.Assertion;27import gc.g1.unloading.check.ClassAssertion;28import gc.g1.unloading.check.PhantomizedAssertion;2930import gc.g1.unloading.check.FinalizedAssertion;31import gc.g1.unloading.check.PhantomizationServiceThread;32import gc.g1.unloading.check.cleanup.UnusedThreadKiller;33import gc.g1.unloading.classloaders.DoItYourselfClassLoader;34import gc.g1.unloading.classloaders.FinalizableClassloader;35import gc.g1.unloading.classloaders.JNIClassloader;36import gc.g1.unloading.classloaders.ReflectionClassloader;37import gc.g1.unloading.configuration.ClassloadingMethod;38import gc.g1.unloading.configuration.KeepRefMode;39import gc.g1.unloading.configuration.TestConfiguration;40import gc.g1.unloading.keepref.*;41import nsk.share.test.ExecutionController;42import sun.hotspot.WhiteBox;4344import java.lang.invoke.MethodHandles;45import java.lang.invoke.MethodHandles.Lookup;4647import java.lang.ref.*;48import java.lang.reflect.Field;49import java.lang.reflect.InvocationTargetException;50import java.lang.reflect.Method;51import java.util.Collection;52import java.util.LinkedList;53import java.util.Random;5455/**56* This helper performs dirty job: loads classes, instantiate objects, performs redefinition etc...57*/58public class ClassLoadingHelper {5960private static final int NATIVE_VERBOSITY = 2;6162private static final Object[] NO_CP_PATCHES = new Object[0];6364private static BytecodeFactory bf;6566private ExecutionController executionController;6768private PhantomizationServiceThread phantomizationServiceThread;6970private Random random;7172private TestConfiguration configuration;7374// This should be TRUE for bytecode generators that use ASM to create the bytecodes75// instead of creating Java source and then compiling with InMemoryJavaCompiler.76private boolean prepend_package = true;7778/**79* Constructor that creates instance of helper. All arguments are self-explaining.80* @param executionController81* @param randomSeed82* @param testConfiguration83*/84public ClassLoadingHelper(ExecutionController executionController,85long randomSeed, TestConfiguration testConfiguration) {86random = new Random(randomSeed);87this.executionController = executionController;88this.configuration = testConfiguration;8990phantomizationServiceThread = new PhantomizationServiceThread(executionController);91Thread thread = new Thread(phantomizationServiceThread);92thread.setDaemon(true);93thread.start();9495if (configuration.isInMemoryCompilation() && !configuration.isHumongousClass() && !(configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF)) {96prepend_package = false;97bf = new BytecodeGeneratorFactory(random.nextLong());98} else {99if (configuration.isHumongousClass()) {100bf = new BytecodeMutatorFactory(HumongousTemplateClass.class.getName());101} else if (configuration.getKeepRefMode() == KeepRefMode.THREAD_ITSELF) {102bf = new BytecodeMutatorFactory(ThreadTemplateClass.class.getName());103} else {104bf = new BytecodeMutatorFactory();105}106}107}108109/**110* Load class that's supposed to live. Method returns collection of assertions to check it will live.111* @param className_112* @return113*/114public Collection<Assertion> loadClassThatGonnaLive(String className_) {115// if generating the bytecodes using ASM then prepend the package name to116// the classname so that, when created as a hidden class, it will be in the117// same package as its lookup class.118Bytecode kit = bf.createBytecode(prepend_package ?119"gc.g1.unloading.loading." + className_ : className_);120String className = kit.getClassName();121byte[] bytecode = kit.getBytecode();122Class<?> clazz = loadClass(className, bytecode);123Object object = instantiateObject(clazz);124Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object);125126redefineIfNeeded(bytecode, clazz);127128warmUpClassIfNeeded(object);129Assertion assertion;130assertion = new ClassAssertion(className, true);131132switch (configuration.getKeepRefMode()) {133case STRONG_REFERENCE:134assertion.keepLink(referenceToKeep);135break;136case SOFT_REFERENCE:137assertion.keepLink(new SoftReference<Object>(referenceToKeep));138break;139case STATIC_FIELD:140RefHolder holder1 = new InStaticFieldHolder();141assertion.keepLink(holder1.hold(referenceToKeep));142break;143case STACK_LOCAL:144RefHolder holder2 = new InStackLocalHolder(); // UnusedThreadKiller145assertion.keepLink(holder2.hold(referenceToKeep));146break;147case THREAD_FIELD:148RefHolder holder3 = new InThreadFieldHolder(); // UnusedThreadKiller149assertion.keepLink(holder3.hold(referenceToKeep));150break;151case THREAD_ITSELF:152Thread objectThread = (Thread) object;153objectThread.setDaemon(true);154objectThread.start();155assertion.keepLink(new UnusedThreadKiller(objectThread.getId())); // UnusedThreadKiller156break;157case STATIC_FIELD_OF_ROOT_CLASS:158RefHolder holder4 = new NullClassloaderHolder(random.nextLong());159Object keep = holder4.hold(referenceToKeep);160if (keep != null) {161assertion.keepLink(keep);162}163break;164case JNI_GLOBAL_REF:165JNIGlobalRefHolder holder5 = new JNIGlobalRefHolder();166assertion.keepLink(holder5.hold(referenceToKeep));167break;168case JNI_LOCAL_REF:169JNILocalRefHolder holder6 = new JNILocalRefHolder();170assertion.keepLink(holder6.hold(referenceToKeep));171break;172}173174Collection<Assertion> returnValue = new LinkedList<>();175returnValue.add(assertion);176return returnValue;177}178179/**180* Load class that's supposed to be unloaded. Method returns collection of assertions to check it will be unloaded.181* @param className_182* @return183*/184public Collection<Assertion> loadClassThatGonnaDie(String className_) {185Collection<Assertion> returnValue = new LinkedList<>();186// if generating the bytecodes using ASM then prepend the package name to187// the classname so that, when created as a hidden class, it will be in the188// same package as its lookup class.189Bytecode kit = bf.createBytecode(prepend_package ?190"gc.g1.unloading.loading." + className_ : className_);191String className = kit.getClassName();192byte[] bytecode = kit.getBytecode();193Class<?> clazz = loadClass(className, bytecode);194FinalizableClassloader cl = null;195if (clazz.getClassLoader() instanceof FinalizableClassloader) {196cl = (FinalizableClassloader) clazz.getClassLoader();197}198Object object = instantiateObject(clazz);199Object referenceToKeep = configuration.getWhatToKeep().decideUponRefToKeep(clazz, clazz.getClassLoader(), object);200201redefineIfNeeded(bytecode, clazz);202203warmUpClassIfNeeded(object);204Assertion assertion;205assertion = new ClassAssertion(className, false);206switch (configuration.getReleaseRefMode()) {207case WEAK:208assertion.keepLink(new WeakReference<Object>(referenceToKeep));209break;210case PHANTOM:211final ReferenceQueue queue = new ReferenceQueue<Object>();212assertion.keepLink(new PhantomReference<Object>(referenceToKeep, queue));213new Thread(new ReferenceCleaningThread(executionController, queue)).start();214break;215}216returnValue.add(assertion);217218if (cl != null) {219// Check that classloader will be finalized220FinalizedAssertion finalizedAssertion = new FinalizedAssertion();221cl.setFinalizedAssertion(finalizedAssertion);222returnValue.add(finalizedAssertion);223224// Check that classloader will be phantomized225PhantomizedAssertion phantomizedAssertion = new PhantomizedAssertion();226PhantomReference phantomReference = new PhantomReference<Object>(cl, phantomizationServiceThread.getQueue());227phantomizationServiceThread.add(phantomReference, phantomizedAssertion);228returnValue.add(phantomizedAssertion);229}230return returnValue;231}232233private void redefineIfNeeded(byte[] bytecode, Class<?> clazz) {234if (configuration.isRedefineClasses()) {235BytecodePatcher.patch(bytecode);236makeRedefinition(NATIVE_VERBOSITY, clazz, bytecode);237238// This will call class's method239instantiateObject(clazz);240}241}242243private Class<?> loadClass(String className, byte[] bytecode) {244try {245switch (configuration.getClassloadingMethod()) {246case PLAIN:247DoItYourselfClassLoader loader1 = new DoItYourselfClassLoader();248return loader1.defineClass(className, bytecode);249case REFLECTION:250return Class.forName(className, true, new ReflectionClassloader(bytecode, className));251case JNI:252return JNIClassloader.loadThroughJNI(className, bytecode);253case HIDDEN_CLASSLOADER:254Lookup lookup = MethodHandles.lookup();255return lookup.defineHiddenClass(bytecode, true).lookupClass();256}257return null;258} catch (ClassNotFoundException | IllegalAccessException e) {259throw new RuntimeException("Test bug!", e);260}261}262263private Object instantiateObject(Class<?> clazz) {264try {265Object object = clazz.newInstance();266267// Call method just for fun268for (Method m : clazz.getMethods()) {269if (m.getName().equals("main")) {270m.invoke(object);271}272}273return object;274} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {275throw new RuntimeException("Test bug!", e);276}277}278279private void warmUpClassIfNeeded(Object object) {280if (configuration.getCompilationLevel() < 1 || configuration.getCompilationNumber() == 0) {281return;282}283Method m = null;284for (Method method : object.getClass().getMethods()) {285if (method.getName().equalsIgnoreCase("methodForCompilation")) {286m = method;287}288}289WhiteBox wb = WhiteBox.getWhiteBox();290if (!wb.isMethodCompilable(m)) {291throw new RuntimeException("Test bug! Method occured to be not compilable. Requires investigation.");292}293294for (int i = configuration.getCompilationNumber(); i >= 0 && executionController.continueExecution(); i--) {295if (!wb.isMethodCompilable(m, configuration.getCompilationLevel())) {296continue;297}298if (!wb.enqueueMethodForCompilation(m, configuration.getCompilationLevel())) {299throw new RuntimeException("Method could not be enqueued for compilation at level " + configuration.getCompilationLevel());300}301while (!wb.isMethodCompiled(m) && executionController.continueExecution()) {302sleep(50);303try {304m.invoke(object, new Object());305} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {306throw new RuntimeException("Something went wrong while compilation", e);307}308}309if (i > 0) {310wb.deoptimizeMethod(m);311}312}313}314315native static int makeRedefinition0(int verbose, Class<?> redefClass, byte[] classBytes);316317private static void makeRedefinition(int verbose, Class<?> redefClass, byte[] classBytes) {318new LibLoader().hashCode();319if (makeRedefinition0(verbose, redefClass, classBytes) != 0) {320throw new RuntimeException("Test bug: native method \"makeRedefinition\" return nonzero");321}322}323324private static void sleep(long millis) {325try {326Thread.sleep(millis);327} catch (InterruptedException e) {328throw new RuntimeException("Got InterruptedException while sleeping.", e);329}330}331332}333334class ReferenceCleaningThread extends ExecutionTask {335336private ReferenceQueue<?> queue;337338public ReferenceCleaningThread(ExecutionController executionController, ReferenceQueue<?> queue) {339super(executionController);340this.queue = queue;341}342343@Override344protected void task() throws Exception {345Reference<?> ref = queue.remove(100);346if (ref != null) {347ref.clear();348return;349}350}351352}353354355