OpenJDK / amber / amber
changeset 60271:83ff8e2fa7ab
8236938: [TESTBUG] JFR event MetaspaceAllocationFailure is not tested
Summary: New test for MetaspaceAllocationFailure
Reviewed-by: hseigel, stuefe
author | mseledtsov |
---|---|
date | Mon, 02 Mar 2020 12:16:18 -0800 |
parents | ac3371755ede |
children | 80e4a066342d |
files | test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java test/hotspot/jtreg/runtime/testlibrary/GeneratedClassLoader.java test/jdk/jdk/jfr/event/runtime/TestMetaspaceAllocationFailure.java test/lib/jdk/test/lib/classloader/GeneratingCompilingClassLoader.java |
diffstat | 4 files changed, 282 insertions(+), 212 deletions(-) [+] |
line wrap: on
line diff
--- a/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java Mon Mar 02 14:45:52 2020 -0500 +++ b/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java Mon Mar 02 12:16:18 2020 -0800 @@ -23,13 +23,14 @@ /** * @test - * @library /runtime/testlibrary + * @library /test/lib * @modules java.base/jdk.internal.misc * @modules java.compiler * @run main/othervm/timeout=200 -Xmx1g FragmentMetaspace */ import java.io.IOException; +import jdk.test.lib.classloader.GeneratingCompilingClassLoader; /** * Test that tries to fragment the native memory used by class loaders. @@ -53,7 +54,7 @@ long startTime = System.currentTimeMillis(); for (int i = 0; System.currentTimeMillis() < startTime + time && i < iterations; ++i) { try { - GeneratedClassLoader gcl = new GeneratedClassLoader(); + GeneratingCompilingClassLoader gcl = new GeneratingCompilingClassLoader(); // getGeneratedClasses throws a RuntimeException in cases where // the javac exit code is not 0. If the original reason for the exception is
--- a/test/hotspot/jtreg/runtime/testlibrary/GeneratedClassLoader.java Mon Mar 02 14:45:52 2020 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2013, 2018, 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. - */ - -import java.io.DataInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import javax.tools.JavaCompiler; -import javax.tools.ToolProvider; - -/** - * A class loader that generates new classes. - * The generated classes are made by first emitting java sources with nested - * static classes, these are then compiled and the class files are read back. - * Some efforts are made to make the class instances unique and of not insignificant - * size. - */ -public class GeneratedClassLoader extends ClassLoader { - /** - * Holds a pair of class bytecodes and class name (for use with defineClass). - */ - private static class GeneratedClass { - public byte[] bytes; - public String name; - public GeneratedClass(byte[] bytes, String name) { - this.bytes = bytes; this.name = name; - } - } - - /** - * Used to uniquely name every class generated. - */ - private static int count = 0; - /** - * Used to enable/disable keeping the class files and java sources for - * the generated classes. - */ - private static boolean deleteFiles = Boolean.parseBoolean( - System.getProperty("GeneratedClassLoader.deleteFiles", "true")); - - private static String bigstr = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " - + "In facilisis scelerisque vehicula. Donec congue nisi a " - + "leo posuere placerat lobortis felis ultrices. Pellentesque " - + "habitant morbi tristique senectus et netus et malesuada " - + "fames ac turpis egestas. Nam tristique velit at felis " - + "iaculis at tempor sem vestibulum. Sed adipiscing lectus " - + "non mi molestie sagittis. Morbi eu purus urna. Nam tempor " - + "tristique massa eget semper. Mauris cursus, nulla et ornare " - + "vehicula, leo dolor scelerisque metus, sit amet rutrum erat " - + "sapien quis dui. Nullam eleifend risus et velit accumsan sed " - + "suscipit felis pulvinar. Nullam faucibus suscipit gravida. " - + "Pellentesque habitant morbi tristique senectus et netus et " - + "malesuada fames ac turpis egestas. Nullam ut massa augue, " - + "nec viverra mauris."; - - private static int getNextCount() { - return count++; - } - - ////// end statics - - private JavaCompiler javac; - private String nameBase; - - public GeneratedClassLoader() { - javac = ToolProvider.getSystemJavaCompiler(); - nameBase = "TestSimpleClass"; - } - - private long getBigValue(int which) { - // > 65536 is too large to encode in the bytecode - // so this will force us to emit a constant pool entry for this int - return (long)which + 65537; - } - - private String getBigString(int which) { - return bigstr + which; - } - - private String getClassName(int count) { - return nameBase + count; - } - - private String generateSource(int count, int sizeFactor, int numClasses) { - StringBuilder sb = new StringBuilder(); - sb.append("public class ").append(getClassName(count)).append("{\n"); - for (int j = 0; j < numClasses; ++j) { - sb.append("public static class ") - .append("Class") - .append(j) - .append("{\n"); - for (int i = 0; i < sizeFactor; ++i) { - int value = i; - sb.append("private long field") - .append(i).append(" = ") - .append(getBigValue(value++)) - .append(";\n"); - sb.append("public long method") - .append(i) - .append("() {\n"); - sb.append("return ") - .append(getBigValue(value++)) - .append(";"); - sb.append("}\n"); - sb.append("private String str").append(i) - .append(" = \"") - .append(getBigString(i)) - .append("\";"); - } - sb.append("\n}"); - } - sb.append("\n}"); - return sb.toString(); - } - - private GeneratedClass[] getGeneratedClass(int sizeFactor, int numClasses) throws IOException { - int uniqueCount = getNextCount(); - String src = generateSource(uniqueCount, sizeFactor, numClasses); - String className = getClassName(uniqueCount); - File file = new File(className + ".java"); - try (PrintWriter pw = new PrintWriter(new FileWriter(file))) { - pw.append(src); - pw.flush(); - } - ByteArrayOutputStream err = new ByteArrayOutputStream(); - int exitcode = javac.run(null, null, err, file.getCanonicalPath()); - if (exitcode != 0) { - // Print Error - System.err.print(err); - if (err.toString().contains("java.lang.OutOfMemoryError: Java heap space")) { - throw new OutOfMemoryError("javac failed with resources exhausted"); - } else { - throw new RuntimeException("javac failure when compiling: " + - file.getCanonicalPath()); - } - } else { - if (deleteFiles) { - file.delete(); - } - } - GeneratedClass[] gc = new GeneratedClass[numClasses]; - for (int i = 0; i < numClasses; ++i) { - String name = className + "$" + "Class" + i; - File classFile = new File(name + ".class"); - byte[] bytes; - try (DataInputStream dis = new DataInputStream(new FileInputStream(classFile))) { - bytes = new byte[dis.available()]; - dis.readFully(bytes); - } - if (deleteFiles) { - classFile.delete(); - } - gc[i] = new GeneratedClass(bytes, name); - } - if (deleteFiles) { - new File(className + ".class").delete(); - } - return gc; - } - - /** - * Generate a single class, compile it and load it. - * @param sizeFactor Fuzzy measure of how large the class should be. - * @return the Class instance. - * @throws IOException - */ - public Class<?> generateClass(int sizeFactor) throws IOException { - return getGeneratedClasses(sizeFactor, 1)[0]; - } - - /** - * Generate several classes, compile and load them. - * @param sizeFactor Fuzzy measure of how large each class should be. - * @param numClasses The number of classes to create - * @return an array of the Class instances. - * @throws IOException - */ - public Class<?>[] getGeneratedClasses(int sizeFactor, int numClasses) throws IOException { - GeneratedClass[] gc = getGeneratedClass(sizeFactor, numClasses); - Class<?>[] classes = new Class[numClasses]; - for (int i = 0; i < numClasses; ++i) { - classes[i] = defineClass(gc[i].name, gc[i].bytes, 0 , gc[i].bytes.length); - } - return classes; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/event/runtime/TestMetaspaceAllocationFailure.java Mon Mar 02 12:16:18 2020 -0800 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, 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 + * @summary Test jdk.MetaspaceAllocationFailure event + * @library /test/lib + * @modules java.base/jdk.internal.misc java.compiler + * @run main/othervm -Xmx1G -XX:MaxMetaspaceSize=200M + * -XX:StartFlightRecording -Xshare:off + * jdk.jfr.event.runtime.TestMetaspaceAllocationFailure + * @run main/othervm -Xmx1G -XX:CompressedClassSpaceSize=200M + * -XX:StartFlightRecording -Xshare:off + * jdk.jfr.event.runtime.TestMetaspaceAllocationFailure + */ +package jdk.jfr.event.runtime; + +import java.util.concurrent.atomic.AtomicBoolean; +import jdk.jfr.consumer.RecordingStream; +import jdk.test.lib.classloader.GeneratingCompilingClassLoader; +import jdk.test.lib.Asserts; + + +public class TestMetaspaceAllocationFailure { + public static void main(String... args) throws Exception { + AtomicBoolean eventArrived = new AtomicBoolean(false); + try (RecordingStream r = new RecordingStream()) { + r.onEvent("jdk.MetaspaceAllocationFailure", e -> eventArrived.set(true)); + r.startAsync(); + + try { + while (!eventArrived.get()) { + GeneratingCompilingClassLoader cl = new GeneratingCompilingClassLoader(); + cl.getGeneratedClasses(50, 20); + Thread.sleep(100); + System.out.println("Iteration"); + } + System.gc(); + System.out.println("main(): Event arrived"); + } catch (OutOfMemoryError e) { + System.gc(); + System.out.println("main(): OutOfMemoryError (expected): " + e.getMessage()); + } + + Asserts.assertTrue(eventArrived.get()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/jdk/test/lib/classloader/GeneratingCompilingClassLoader.java Mon Mar 02 12:16:18 2020 -0800 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013, 2020, 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. + */ +package jdk.test.lib.classloader; + +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; + +/** + * A class loader that generates new classes. + * The generated classes are made by first emitting java sources with nested + * static classes, these are then compiled and the class files are read back. + * Some efforts are made to make the class instances unique and of not insignificant + * size. + */ +public class GeneratingCompilingClassLoader extends ClassLoader { + /** + * Holds a pair of class bytecodes and class name (for use with defineClass). + */ + private static class GeneratedClass { + public byte[] bytes; + public String name; + public GeneratedClass(byte[] bytes, String name) { + this.bytes = bytes; this.name = name; + } + } + + /** + * Used to uniquely name every class generated. + */ + private static int count = 0; + /** + * Used to enable/disable keeping the class files and java sources for + * the generated classes. + */ + private static boolean deleteFiles = Boolean.parseBoolean( + System.getProperty("GeneratingCompilingClassLoader.deleteFiles", "true")); + + private static String bigstr = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + + "In facilisis scelerisque vehicula. Donec congue nisi a " + + "leo posuere placerat lobortis felis ultrices. Pellentesque " + + "habitant morbi tristique senectus et netus et malesuada " + + "fames ac turpis egestas. Nam tristique velit at felis " + + "iaculis at tempor sem vestibulum. Sed adipiscing lectus " + + "non mi molestie sagittis. Morbi eu purus urna. Nam tempor " + + "tristique massa eget semper. Mauris cursus, nulla et ornare " + + "vehicula, leo dolor scelerisque metus, sit amet rutrum erat " + + "sapien quis dui. Nullam eleifend risus et velit accumsan sed " + + "suscipit felis pulvinar. Nullam faucibus suscipit gravida. " + + "Pellentesque habitant morbi tristique senectus et netus et " + + "malesuada fames ac turpis egestas. Nullam ut massa augue, " + + "nec viverra mauris."; + + private static int getNextCount() { + return count++; + } + + ////// end statics + + private JavaCompiler javac; + private String nameBase; + + public GeneratingCompilingClassLoader() { + javac = ToolProvider.getSystemJavaCompiler(); + nameBase = "TestSimpleClass"; + } + + private long getBigValue(int which) { + // > 65536 is too large to encode in the bytecode + // so this will force us to emit a constant pool entry for this int + return (long)which + 65537; + } + + private String getBigString(int which) { + return bigstr + which; + } + + private String getClassName(int count) { + return nameBase + count; + } + + private String generateSource(int count, int sizeFactor, int numClasses) { + StringBuilder sb = new StringBuilder(); + sb.append("public class ").append(getClassName(count)).append("{\n"); + for (int j = 0; j < numClasses; ++j) { + sb.append("public static class ") + .append("Class") + .append(j) + .append("{\n"); + for (int i = 0; i < sizeFactor; ++i) { + int value = i; + sb.append("private long field") + .append(i).append(" = ") + .append(getBigValue(value++)) + .append(";\n"); + sb.append("public long method") + .append(i) + .append("() {\n"); + sb.append("return ") + .append(getBigValue(value++)) + .append(";"); + sb.append("}\n"); + sb.append("private String str").append(i) + .append(" = \"") + .append(getBigString(i)) + .append("\";"); + } + sb.append("\n}"); + } + sb.append("\n}"); + return sb.toString(); + } + + private GeneratedClass[] getGeneratedClass(int sizeFactor, int numClasses) throws IOException { + int uniqueCount = getNextCount(); + String src = generateSource(uniqueCount, sizeFactor, numClasses); + String className = getClassName(uniqueCount); + File file = new File(className + ".java"); + try (PrintWriter pw = new PrintWriter(new FileWriter(file))) { + pw.append(src); + pw.flush(); + } + ByteArrayOutputStream err = new ByteArrayOutputStream(); + int exitcode = javac.run(null, null, err, file.getCanonicalPath()); + if (exitcode != 0) { + // Print Error + System.err.print(err); + if (err.toString().contains("java.lang.OutOfMemoryError: Java heap space")) { + throw new OutOfMemoryError("javac failed with resources exhausted"); + } else { + throw new RuntimeException("javac failure when compiling: " + + file.getCanonicalPath()); + } + } else { + if (deleteFiles) { + file.delete(); + } + } + GeneratedClass[] gc = new GeneratedClass[numClasses]; + for (int i = 0; i < numClasses; ++i) { + String name = className + "$" + "Class" + i; + File classFile = new File(name + ".class"); + byte[] bytes; + try (DataInputStream dis = new DataInputStream(new FileInputStream(classFile))) { + bytes = new byte[dis.available()]; + dis.readFully(bytes); + } + if (deleteFiles) { + classFile.delete(); + } + gc[i] = new GeneratedClass(bytes, name); + } + if (deleteFiles) { + new File(className + ".class").delete(); + } + return gc; + } + + /** + * Generate a single class, compile it and load it. + * @param sizeFactor Fuzzy measure of how large the class should be. + * @return the Class instance. + * @throws IOException + */ + public Class<?> generateClass(int sizeFactor) throws IOException { + return getGeneratedClasses(sizeFactor, 1)[0]; + } + + /** + * Generate several classes, compile and load them. + * @param sizeFactor Fuzzy measure of how large each class should be. + * @param numClasses The number of classes to create + * @return an array of the Class instances. + * @throws IOException + */ + public Class<?>[] getGeneratedClasses(int sizeFactor, int numClasses) throws IOException { + GeneratedClass[] gc = getGeneratedClass(sizeFactor, numClasses); + Class<?>[] classes = new Class[numClasses]; + for (int i = 0; i < numClasses; ++i) { + classes[i] = defineClass(gc[i].name, gc[i].bytes, 0 , gc[i].bytes.length); + } + return classes; + } +}