changeset 59584:580f082b9fc1 nestmates

[nestmates] add more tests to verify unloading of hidden classes
author mchung
date Thu, 26 Mar 2020 14:06:29 -0700
parents cd8f44ac380d
children 33727eb2c5b0
files test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java
diffstat 1 files changed, 111 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java	Thu Mar 26 18:22:26 2020 +0000
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java	Thu Mar 26 14:06:29 2020 -0700
@@ -51,6 +51,7 @@
 import org.testng.annotations.Test;
 
 import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
 import static org.testng.Assert.*;
 
 public class UnloadingTest {
@@ -76,7 +77,7 @@
         Class<?> helper = Class.forName("LookupHelper", true, loader);
         Method m = helper.getMethod("getLookup");
         Lookup lookup = (Lookup)m.invoke(null);
-        HiddenClassUnloader unloader = new HiddenClassUnloader(lookup, false);
+        HiddenClassUnloader unloader = createHiddenClass(lookup, false);
         // the hidden class should be unloaded
         unloader.unload();
 
@@ -93,21 +94,120 @@
         Class<?> helper = Class.forName("LookupHelper", true, loader);
         Method m = helper.getMethod("getLookup");
         Lookup lookup = (Lookup)m.invoke(null);
-        HiddenClassUnloader unloader = new HiddenClassUnloader(lookup, true);
+        HiddenClassUnloader unloader = createHiddenClass(lookup, true);
         assertFalse(unloader.tryUnload());      // hidden class is not unloaded
+
+        // loader is strongly reachable
+        Reference.reachabilityFence(loader);
+    }
+
+    /*
+     * Create a nest of two hidden classes.
+     * They can be unloaded even the loader is strongly reachable
+     */
+    @Test
+    public void hiddenClassNest2() throws Exception {
+        TestLoader loader = new TestLoader();
+        Class<?> helper = Class.forName("LookupHelper", true, loader);
+        Method m = helper.getMethod("getLookup");
+        Lookup lookup = (Lookup)m.invoke(null);
+        HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, false);
+
+        // keep a strong reference to the nest member class
+        Class<?> member = unloaders[1].weakRef.get();
+        // nest host and member will not be unloaded
+        assertFalse(unloaders[0].tryUnload());
+        assertFalse(unloaders[1].tryUnload());
+
+        // clear the reference to the nest member
+        member = null;
+
+        // nest host and member will be unloaded
+        unloaders[0].unload();
+        unloaders[1].unload();
+
+        // loader is strongly reachable
+        Reference.reachabilityFence(loader);
+    }
+
+    /*
+     * Create a nest with a hidden class nest host and strong nest member.
+     * Test that both are not unloaded
+     */
+    @Test
+    public void hiddenClassNestStrongMember() throws Exception {
+        TestLoader loader = new TestLoader();
+        Class<?> helper = Class.forName("LookupHelper", true, loader);
+        Method m = helper.getMethod("getLookup");
+        Lookup lookup = (Lookup)m.invoke(null);
+        HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, true);
+        assertFalse(unloaders[0].tryUnload());      // nest host cannot be unloaded
+        assertFalse(unloaders[1].tryUnload());      // nest member cannot be unloaded
+
+        // loader is strongly reachable
+        Reference.reachabilityFence(loader);
+    }
+
+    /*
+     * Create a nest with a strong hidden nest host and a hidden class member.
+     * The nest member can be unloaded whereas the nest host will not be unloaded.
+     */
+    @Test
+    public void hiddenClassNestStrongHost() throws Exception {
+        TestLoader loader = new TestLoader();
+        Class<?> helper = Class.forName("LookupHelper", true, loader);
+        Method m = helper.getMethod("getLookup");
+        Lookup lookup = (Lookup)m.invoke(null);
+        HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, true, false);
+        assertFalse(unloaders[0].tryUnload());      // nest host cannot be unloaded
+        unloaders[1].unload();
+
+        // loader is strongly reachable
+        Reference.reachabilityFence(loader);
+    }
+
+    /*
+     * Create a HiddenClassUnloader that holds a weak reference to the newly created
+     * hidden class.
+     */
+    static HiddenClassUnloader createHiddenClass(Lookup lookup, boolean strong) throws Exception {
+        Class<?> hc;
+        if (strong) {
+            hc = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG).lookupClass();
+        } else {
+            hc = lookup.defineHiddenClass(hiddenClassBytes, false).lookupClass();
+        }
+        assertTrue(hc.getClassLoader() == lookup.lookupClass().getClassLoader());
+        return new HiddenClassUnloader(hc);
+    }
+
+    /*
+     * Create an array of HiddenClassUnloader with two elements: the first element
+     * is for the nest host and the second element is for the nest member.
+     */
+    static HiddenClassUnloader[] createNestOfTwoHiddenClasses(Lookup lookup, boolean strongHost, boolean strongMember) throws Exception {
+        Lookup hostLookup;
+        if (strongHost) {
+            hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG);
+        } else {
+            hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false);
+        }
+        Class<?> host = hostLookup.lookupClass();
+        Class<?> member;
+        if (strongMember) {
+            member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE, STRONG).lookupClass();
+        } else {
+            member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE).lookupClass();
+        }
+        assertTrue(member.getNestHost() == host);
+        return new HiddenClassUnloader[] { new HiddenClassUnloader(host), new HiddenClassUnloader(member) };
     }
 
     static class HiddenClassUnloader {
-        private final WeakReference<?> weakRef;
-        HiddenClassUnloader(Lookup lookup, boolean strong) throws Exception {
-            Class<?> hc;
-            if (strong) {
-                hc = lookup.defineHiddenClass(hiddenClassBytes, false, Lookup.ClassOption.STRONG).lookupClass();
-            } else {
-                hc = lookup.defineHiddenClass(hiddenClassBytes, false).lookupClass();
-            }
+        private final WeakReference<Class<?>> weakRef;
+        private HiddenClassUnloader(Class<?> hc) {
+            assertTrue(hc.isHiddenClass());
             this.weakRef = new WeakReference<>(hc);
-            assertTrue(hc.getClassLoader() == lookup.lookupClass().getClassLoader());
         }
 
         void unload() {