1 /*
  2 * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
  3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4 *
  5 * This code is free software; you can redistribute it and/or modify it
  6 * under the terms of the GNU General Public License version 2 only, as
  7 * published by the Free Software Foundation.
  8 *
  9 * This code is distributed in the hope that it will be useful, but WITHOUT
 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12 * version 2 for more details (a copy is included in the LICENSE file that
 13 * accompanied this code).
 14 *
 15 * You should have received a copy of the GNU General Public License version
 16 * 2 along with this work; if not, write to the Free Software Foundation,
 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18 *
 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20 * or visit www.oracle.com if you need additional information or have any
 21 * questions.
 22 */
 23 
 24 /**
 25 * @test
 26 * @summary Tests class unloading on virtual threads
 27 *
 28 * @compile --enable-preview -source ${jdk.version} ClassUnloading.java
 29 * @run main/othervm --enable-preview -XX:+UseChunkBitmaps -XX:-UseCompressedOops ClassUnloading
 30 * @run main/othervm --enable-preview -XX:+UseChunkBitmaps -XX:+UseCompressedOops ClassUnloading
 31 * @run main/othervm --enable-preview -XX:+UseChunkBitmaps -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk/internal/vm/Continuation,ClassUnloading ClassUnloading
 32 * @run main/othervm --enable-preview -XX:-UseChunkBitmaps ClassUnloading
 33 */
 34 
 35 // @run testng/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk/internal/vm/Continuation,Basic Basic
 36 
 37 import java.lang.invoke.MethodHandles;
 38 import java.lang.invoke.MethodHandles.Lookup;
 39 import java.lang.ref.Reference;
 40 import java.lang.ref.WeakReference;
 41 import java.net.URI;
 42 import java.nio.file.Files;
 43 import java.nio.file.Path;
 44 import java.util.concurrent.Executors;
 45 import java.util.function.BooleanSupplier;
 46 
 47 public class ClassUnloading {
 48     public static void main(String[] args) throws Throwable {
 49         System.out.println(Thread.currentThread());
 50         test();
 51         System.out.println();
 52         // repeat test in virtual thread
 53         try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
 54             executor.submit(() -> {
 55                 System.out.println(Thread.currentThread());
 56                 test();
 57                 return null;
 58             }).get();
 59         }
 60     }
 61 
 62     static void test() throws Exception {
 63         // class bytes for Dummy class
 64         URI uri = ClassUnloading.class.getProtectionDomain().getCodeSource().getLocation().toURI();
 65         Path file = Path.of(uri).resolve("Dummy.class");
 66         byte[] classBytes = Files.readAllBytes(file);
 67         // define hidden class in same run-time package as this class
 68         Lookup lookup = MethodHandles.lookup();
 69         Class<?> clazz = lookup.defineHiddenClass(classBytes, false).lookupClass();
 70         String cn = clazz.getName();
 71         var ref = new WeakReference<Class<?>>(clazz);
 72         // try to cause hidden class to be unloaded, should fail due to strong ref
 73         System.out.println("try unload " + cn);
 74         boolean unloaded = tryUnload(ref);
 75         if (unloaded)
 76             throw new RuntimeException(cn + " unloaded!!!");
 77         Reference.reachabilityFence(clazz);
 78         clazz = null;
 79         // try to cause hidden class to be unloaded, should succeed
 80         System.out.println("unload " + cn);
 81         unload(ref);
 82     }
 83 
 84     static boolean tryUnload(WeakReference<Class<?>> ref) {
 85         return gc(() -> ref.get() == null);
 86     }
 87 
 88     static void unload(WeakReference<Class<?>> ref) {
 89         boolean cleared = gc(() -> ref.get() == null);
 90         if (!cleared)
 91             throw new RuntimeException("weak reference not cleared!!");
 92         Class<?> clazz = ref.get();
 93         if (clazz != null)
 94             throw new RuntimeException(clazz + " not unloaded");
 95     }
 96 
 97     static boolean gc(BooleanSupplier s) {
 98         try {
 99             for (int i = 0; i < 10; i++) {
100                 if (s.getAsBoolean())
101                     return true;
102                 System.out.print(".");
103                 System.gc();
104                 try {
105                     Thread.sleep(100);
106                 } catch (InterruptedException e) {
107                     throw new RuntimeException(e);
108                 }
109             }
110             return false;
111         } finally {
112             System.out.println();
113         }
114     }
115 }
116 
117 class Dummy { }