/* * Copyright (c) 2021, 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 * @bug 8048190 * @summary Test that the NCDFE saves the stack trace for the original exception * during class initialization with ExceptionInInitializationError, * and doesn't prevent the classes in the stacktrace to be unloaded. * @requires vm.opt.final.ClassUnloading * @modules java.base/jdk.internal.misc * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -Xlog:class+unload -XX:+WhiteBoxAPI InitExceptionUnloadTest */ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.Set; import java.util.List; import jdk.test.whitebox.WhiteBox; import jdk.test.lib.classloader.ClassUnloadCommon; public class InitExceptionUnloadTest { static public class ThrowsRuntimeException { static int x = 1/0; } static public class ThrowsError { static { if (true) throw new Error(); } } static public class SpecialException extends RuntimeException { SpecialException(int count, String message) { super(message + count); } } static public class ThrowsSpecialException { static { if (true) throw new SpecialException(3, "Very Special "); } } static public class ThrowsOOM { static { if (true) { // Actually getting an OOM might be fragile but it was tested. throw new OutOfMemoryError("Java heap space"); } } } private static void verify_stack(Throwable e, String expected, String cause) throws Exception { ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(byteOS); e.printStackTrace(printStream); printStream.close(); String stackTrace = byteOS.toString("ASCII"); if (!stackTrace.contains(expected) || (cause != null && !stackTrace.contains(cause))) { throw new RuntimeException(expected + " and " + cause + " missing from stacktrace"); } } static String[] expected = new String[] { "java.lang.ExceptionInInitializerError", "Caused by: java.lang.ArithmeticException: / by zero", "java.lang.NoClassDefFoundError: Could not initialize class InitExceptionUnloadTest$ThrowsRuntimeException", "Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.ArithmeticException: / by zero [in thread", "java.lang.Error", null, "java.lang.NoClassDefFoundError: Could not initialize class InitExceptionUnloadTest$ThrowsError", "Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.Error [in thread", "java.lang.ExceptionInInitializerError", "Caused by: InitExceptionUnloadTest$SpecialException: Very Special 3", "java.lang.NoClassDefFoundError: Could not initialize class InitExceptionUnloadTest$ThrowsSpecialException", "Caused by: java.lang.ExceptionInInitializerError: Exception InitExceptionUnloadTest$SpecialException: Very Special 3", "java.lang.OutOfMemoryError", "Java heap space", "java.lang.NoClassDefFoundError: Could not initialize class InitExceptionUnloadTest$ThrowsOOM", "Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.OutOfMemoryError: Java heap space [in thread" }; static String[] classNames = new String[] { "InitExceptionUnloadTest$ThrowsRuntimeException", "InitExceptionUnloadTest$ThrowsError", "InitExceptionUnloadTest$ThrowsSpecialException", "InitExceptionUnloadTest$ThrowsOOM" }; public static WhiteBox wb = WhiteBox.getWhiteBox(); static void test() throws Throwable { ClassLoader cl = ClassUnloadCommon.newClassLoader(); int i = 0; for (String className : classNames) { for (int tries = 2; tries-- > 0; ) { System.err.println("--- try to load " + className); try { Class c = cl.loadClass(className); Object inst = c.newInstance(); } catch (Throwable t) { t.printStackTrace(); System.err.println(); System.err.println("Check results"); verify_stack(t, expected[i], expected[i+1]); i += 2; System.err.println(); } } } cl = null; Set aliveClasses = ClassUnloadCommon.triggerUnloading(List.of(classNames)); ClassUnloadCommon.failIf(!aliveClasses.isEmpty(), "should be unloaded: " + aliveClasses); } public static void main(java.lang.String[] unused) throws Throwable { test(); test(); } }