/* * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test Make sure class unloading occur even if ClassLoaderStats VM operations are executed * @bug 8297427 * @requires vm.opt.final.ClassUnloading * @modules java.base/jdk.internal.misc * @library /test/lib classes * @build jdk.test.whitebox.WhiteBox test.Empty test.LoadInParent test.LoadInChild * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xlog:gc*,class+unload=debug UnloadTestDuringClassLoaderStatsVMOperation */ import java.lang.ref.Reference; import java.net.URLClassLoader; import jdk.test.lib.classloader.ClassUnloadCommon; import jdk.test.whitebox.WhiteBox; public class UnloadTestDuringClassLoaderStatsVMOperation { private static final WhiteBox wb = WhiteBox.getWhiteBox(); private static String className = "test.Empty"; private static String parentClassName = "test.LoadInParent"; private static String childClassName = "test.LoadInChild"; public static void main(String args[]) throws Exception { // Create a thread forcing ClassLoaderStats VM operations. var clsThread = new Thread(() -> { while (true) { wb.forceClassLoaderStatsSafepoint(); } }); clsThread.setDaemon(true); clsThread.start(); // Make sure classes can be unloaded even though the class loader // stats VM operation is running. testClassIsUnloaded(); testClassLoadedInParentIsUnloaded(); } public static void testClassIsUnloaded() throws Exception { ClassUnloadCommon.failIf(wb.isClassAlive(className), className + " is not expected to be alive yet"); // Load a test class and verify that it gets unloaded once we do a major collection. var classLoader = ClassUnloadCommon.newClassLoader(); var loaded = classLoader.loadClass(className); var object = loaded.getDeclaredConstructor().newInstance(); ClassUnloadCommon.failIf(!wb.isClassAlive(className), className + " should be loaded and live"); // Using reachabilityFence() to ensure the object is live. If the test // is run with -Xcomp and ergonomically triggered GCs occur the class // could otherwise be unloaded before verified to be alive above. Reference.reachabilityFence(object); System.out.println("testClassIsUnloaded loaded klass: " + className); // Make class unloadable. classLoader = null; loaded = null; object = null; // Full/Major collection should always unload classes. wb.fullGC(); ClassUnloadCommon.failIf(wb.isClassAlive(className), className + " should have been unloaded"); } public static void testClassLoadedInParentIsUnloaded() throws Exception { ClassUnloadCommon.failIf(wb.isClassAlive(parentClassName), parentClassName + " is not expected to be alive yet"); ClassUnloadCommon.failIf(wb.isClassAlive(childClassName), childClassName + " is not expected to be alive yet"); // Create two class loaders and load a test class in the parent and // verify that it gets unloaded once we do a major collection. var parentClassLoader = ClassUnloadCommon.newClassLoader(); var childClassLoader = new ChildURLClassLoader((URLClassLoader) parentClassLoader); var loadedParent = parentClassLoader.loadClass(parentClassName); var loadedChild = childClassLoader.loadClass(childClassName); var parent = loadedParent.getDeclaredConstructor().newInstance(); var child = loadedChild.getDeclaredConstructor().newInstance(); ClassUnloadCommon.failIf(!wb.isClassAlive(parentClassName), parentClassName + " should be loaded and live"); ClassUnloadCommon.failIf(!wb.isClassAlive(childClassName), childClassName + " should be loaded and live"); // Using reachabilityFence() to ensure the objects are live. If the test // is run with -Xcomp and ergonomically triggered GCs occur they could // otherwise be unloaded before verified to be alive above. Reference.reachabilityFence(parent); Reference.reachabilityFence(child); System.out.println("testClassLoadedInParentIsUnloaded loaded klass: " + loadedParent); System.out.println("testClassLoadedInParentIsUnloaded loaded klass: " + loadedChild); // Clear to allow unloading. parentClassLoader = null; childClassLoader = null; loadedParent = null; loadedChild = null; parent = null; child = null; // Full/Major collection should always unload classes. wb.fullGC(); ClassUnloadCommon.failIf(wb.isClassAlive(parentClassName), parentClassName + " should have been unloaded"); ClassUnloadCommon.failIf(wb.isClassAlive(childClassName), childClassName + " should have been unloaded"); } static class ChildURLClassLoader extends URLClassLoader { public ChildURLClassLoader(URLClassLoader parent) { super("ChildURLClassLoader", parent.getURLs(), parent); } @Override public Class loadClass(String cn, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(cn)) { Class c = findLoadedClass(cn); if (c == null) { try { c = findClass(cn); } catch (ClassNotFoundException e) { c = getParent().loadClass(cn); } } if (resolve) { resolveClass(c); } return c; } } } }