< prev index next >

test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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.

@@ -27,51 +27,58 @@
  * @library /test/lib
  * @modules java.base/jdk.internal.misc
  *          java.compiler
  *          java.management
  *          jdk.internal.jvmstat/sun.jvmstat.monitor
- * @run testng ClassLoaderStatsTest
+ * @run testng/othervm --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest
  */
 
 import org.testng.annotations.Test;
 import org.testng.Assert;
 
 import jdk.test.lib.process.OutputAnalyzer;
 import jdk.test.lib.dcmd.CommandExecutor;
 import jdk.test.lib.dcmd.JMXExecutor;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Iterator;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import jdk.internal.misc.Unsafe;
+
 public class ClassLoaderStatsTest {
 
+  // Expected output from VM.classloader_stats:
     // ClassLoader         Parent              CLD*               Classes   ChunkSz   BlockSz  Type
-    // 0x00000007c0215928  0x0000000000000000  0x0000000000000000       0         0         0  org.eclipse.osgi.baseadaptor.BaseAdaptor$1
-    // 0x00000007c0009868  0x0000000000000000  0x00007fc52aebcc80       1      6144      3768  sun.reflect.DelegatingClassLoader
-    // 0x00000007c0009868  0x0000000000000000  0x00007fc52b8916d0       1      6144      3688  sun.reflect.DelegatingClassLoader
-    // 0x00000007c0009868  0x00000007c0038ba8  0x00007fc52afb8760       1      6144      3688  sun.reflect.DelegatingClassLoader
-    // 0x00000007c0009868  0x0000000000000000  0x00007fc52afbb1a0       1      6144      3688  sun.reflect.DelegatingClassLoader
-    // 0x0000000000000000  0x0000000000000000  0x00007fc523416070    5019  30060544  29956216  <boot classloader>
-    //                                                                455   1210368    672848   + unsafe anonymous classes
-    // 0x00000007c016b5c8  0x00000007c0038ba8  0x00007fc52a995000       5      8192      5864  org.netbeans.StandardModule$OneModuleClassLoader
-    // 0x00000007c0009868  0x00000007c016b5c8  0x00007fc52ac13640       1      6144      3896  sun.reflect.DelegatingClassLoader
+    // 0x0000000800bd3830  0x000000080037f468  0x00007f001c2ea170       1     10240      4672  ClassLoaderStatsTest$DummyClassLoader
+    //                                                                  1      2048      1080   + unsafe anonymous classes
+    //                                                                  1      2048      1088   + hidden weak classes
+    // 0x0000000000000000  0x0000000000000000  0x00007f00e852d190    1607   4628480   3931216  <boot class loader>
+    //                                                                 38    124928     85856   + hidden weak classes
+    // 0x00000008003b5508  0x0000000000000000  0x00007f001c2d4760       1      6144      4040  jdk.internal.reflect.DelegatingClassLoader
+    // 0x000000080037f468  0x000000080037ee80  0x00007f00e868e3f0     228   1368064   1286672  jdk.internal.loader.ClassLoaders$AppClassLoader
     // ...
 
     static Pattern clLine = Pattern.compile("0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*(.*)");
     static Pattern anonLine = Pattern.compile("\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*.*");
 
     public static DummyClassLoader dummyloader;
 
     public void run(CommandExecutor executor) throws ClassNotFoundException {
 
-        // create a classloader and load our special class
+        // create a classloader and load our special classes
         dummyloader = new DummyClassLoader();
         Class<?> c = Class.forName("TestClass", true, dummyloader);
         if (c.getClassLoader() != dummyloader) {
             Assert.fail("TestClass defined by wrong classloader: " + c.getClassLoader());
         }

@@ -80,28 +87,44 @@
         Iterator<String> lines = output.asLines().iterator();
         while (lines.hasNext()) {
             String line = lines.next();
             Matcher m = clLine.matcher(line);
             if (m.matches()) {
-                // verify that DummyClassLoader has loaded 1 class and 1 anonymous class
+                // verify that DummyClassLoader has loaded 1 class, 1 anonymous class, and 1 hidden class
                 if (m.group(4).equals("ClassLoaderStatsTest$DummyClassLoader")) {
-                    System.out.println("line: " + line);
+                    System.out.println("DummyClassLoader line: " + line);
                     if (!m.group(1).equals("1")) {
                         Assert.fail("Should have loaded 1 class: " + line);
                     }
                     checkPositiveInt(m.group(2));
                     checkPositiveInt(m.group(3));
 
                     String next = lines.next();
-                    System.out.println("next: " + next);
+                    System.out.println("DummyClassLoader next: " + next);
+                    if (!next.contains("unsafe anonymous classes")) {
+                        Assert.fail("Should have an anonymous class");
+                    }
                     Matcher m1 = anonLine.matcher(next);
                     m1.matches();
                     if (!m1.group(1).equals("1")) {
                         Assert.fail("Should have loaded 1 anonymous class, but found : " + m1.group(1));
                     }
                     checkPositiveInt(m1.group(2));
                     checkPositiveInt(m1.group(3));
+
+                    next = lines.next();
+                    System.out.println("DummyClassLoader next: " + next);
+                    if (!next.contains("hidden classes")) {
+                        Assert.fail("Should have a hidden class");
+                    }
+                    Matcher m2 = anonLine.matcher(next);
+                    m2.matches();
+                    if (!m2.group(1).equals("1")) {
+                        Assert.fail("Should have loaded 1 hidden class, but found : " + m2.group(1));
+                    }
+                    checkPositiveInt(m2.group(2));
+                    checkPositiveInt(m2.group(3));
                 }
             }
         }
     }
 

@@ -115,12 +138,11 @@
 
         public static final String CLASS_NAME = "TestClass";
 
         static ByteBuffer readClassFile(String name)
         {
-            File f = new File(System.getProperty("test.classes", "."),
-                              name);
+            File f = new File(System.getProperty("test.classes", "."), name);
             try (FileInputStream fin = new FileInputStream(f);
                  FileChannel fc = fin.getChannel())
             {
                 return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
             } catch (IOException e) {

@@ -161,12 +183,43 @@
     public void jmx() throws ClassNotFoundException {
         run(new JMXExecutor());
     }
 }
 
+class HiddenClass { }
+
 class TestClass {
+    private static final String HCName = "HiddenClass.class";
+    private static final String DIR = System.getProperty("test.classes");
+    static Unsafe unsafe = Unsafe.getUnsafe();
+
     static {
-        // force creation of anonymous class (for the lambdaform)
-        Runnable r = () -> System.out.println("Hello");
-        r.run();
+        try {
+            // Create a hidden weak class and an anonymous class.
+            byte[] klassBuf = readClassFile(DIR + File.separator + HCName);
+            Class<?> hc = defineHiddenClass(klassBuf);
+            Class ac = unsafe.defineAnonymousClass(TestClass.class, klassBuf, new Object[0]);
+        } catch (Throwable e) {
+            throw new RuntimeException("Unexpected exception in TestClass: " + e.getMessage());
+        }
+    }
+
+
+    static byte[] readClassFile(String classFileName) throws Exception {
+        File classFile = new File(classFileName);
+        try (FileInputStream in = new FileInputStream(classFile);
+             ByteArrayOutputStream out = new ByteArrayOutputStream())
+        {
+            int b;
+            while ((b = in.read()) != -1) {
+                out.write(b);
+            }
+            return out.toByteArray();
+        }
+    }
+
+    static Class<?> defineHiddenClass(byte[] bytes) throws Exception {
+        Lookup lookup = MethodHandles.lookup();
+        Class<?> hc = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+        return hc;
     }
 }
< prev index next >