1 /* 2 * Copyright (c) 2023, 2025, 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 /* 26 * @test 27 * @summary Test how various AOT optimizations handle classes that are excluded from the AOT cache. 28 * @requires vm.cds.write.archived.java.heap 29 * @comment work around JDK-8345635 30 * @requires !vm.jvmci.enabled 31 * @library /test/jdk/lib/testlibrary /test/lib 32 * /test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes 33 * @build ExcludedClasses CustyWithLoop 34 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar 35 * TestApp 36 * TestApp$Foo 37 * TestApp$Foo$Bar 38 * TestApp$Foo$ShouldBeExcluded 39 * TestApp$MyInvocationHandler 40 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar 41 * CustyWithLoop 42 * @run driver ExcludedClasses 43 */ 44 45 import java.io.File; 46 import java.lang.reflect.Array; 47 import java.lang.reflect.InvocationHandler; 48 import java.lang.reflect.Method; 49 import java.lang.reflect.Proxy; 50 import java.net.URL; 51 import java.net.URLClassLoader; 52 import java.security.ProtectionDomain; 53 import java.util.Map; 54 55 import jdk.jfr.Event; 56 import jdk.test.lib.cds.CDSAppTester; 57 import jdk.test.lib.helpers.ClassFileInstaller; 58 import jdk.test.lib.process.OutputAnalyzer; 59 60 public class ExcludedClasses { 61 static final String appJar = ClassFileInstaller.getJarPath("app.jar"); 62 static final String mainClass = "TestApp"; 63 64 public static void main(String[] args) throws Exception { 65 { 66 Tester tester = new Tester(); 67 tester.run(new String[] {"AOT"} ); 68 } 69 { 70 Tester tester = new Tester(); 71 tester.run(new String[] {"LEYDEN"} ); 72 } 73 } 74 75 static class Tester extends CDSAppTester { 76 public Tester() { 77 super(mainClass); 78 } 79 80 @Override 81 public String classpath(RunMode runMode) { 82 return appJar; 83 } 84 85 @Override 86 public String[] vmArgs(RunMode runMode) { 87 return new String[] { 88 "-Xlog:cds+resolve=trace", 89 }; 90 } 91 92 @Override 93 public String[] appCommandLine(RunMode runMode) { 94 return new String[] { 95 mainClass, runMode.name() 96 }; 97 } 98 99 @Override 100 public void checkExecution(OutputAnalyzer out, RunMode runMode) { 101 if (isDumping(runMode)) { 102 out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I"); 103 } 104 } 105 } 106 } 107 108 class TestApp { 109 static volatile Object custInstance; 110 static volatile Object custArrayInstance; 111 112 public static void main(String args[]) throws Exception { 113 // In new workflow, classes from custom loaders are passed from the preimage 114 // to the final image. See ClassPrelinker::record_unregistered_klasses(). 115 custInstance = initFromCustomLoader(); 116 custArrayInstance = Array.newInstance(custInstance.getClass(), 0); 117 System.out.println(custArrayInstance); 118 System.out.println("Counter = " + Foo.hotSpot()); 119 } 120 121 static Object initFromCustomLoader() throws Exception { 122 String path = "cust.jar"; 123 URL url = new File(path).toURI().toURL(); 124 URL[] urls = new URL[] {url}; 125 URLClassLoader urlClassLoader = 126 new URLClassLoader("MyLoader", urls, null); 127 Class c = Class.forName("CustyWithLoop", true, urlClassLoader); 128 return c.newInstance(); 129 } 130 131 static class MyInvocationHandler implements InvocationHandler { 132 volatile static int cnt; 133 134 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 135 long start = System.currentTimeMillis(); 136 while (System.currentTimeMillis() - start < 20) { 137 cnt += 2; 138 for (int i = 0; i < 1000; i++) { 139 int n = cnt - 2; 140 if (n < 2) { 141 n = 2; 142 } 143 cnt += (i + cnt) % n + cnt % 2; 144 } 145 } 146 return Integer.valueOf(cnt); 147 } 148 } 149 150 static class Foo { 151 volatile static int counter; 152 static Class c = ShouldBeExcluded.class; 153 154 static Map mapProxy = (Map) Proxy.newProxyInstance( 155 Foo.class.getClassLoader(), 156 new Class[] { Map.class }, 157 new MyInvocationHandler()); 158 159 static int hotSpot() { 160 ShouldBeExcluded s = new ShouldBeExcluded(); 161 Bar b = new Bar(); 162 163 long start = System.currentTimeMillis(); 164 while (System.currentTimeMillis() - start < 1000) { 165 lambdaHotSpot(); 166 s.hotSpot2(); 167 b.hotSpot3(); 168 169 // In JDK mainline, generated proxy classes are excluded from the AOT cache. 170 // In Leyden/premain, generated proxy classes included. The following code should 171 // work with either repos. 172 Integer i = (Integer)mapProxy.get(null); 173 counter += i.intValue(); 174 175 if (custInstance != null) { 176 // Classes loaded by custom loaders are included included in the AOT cache 177 // but their array classes are excluded. 178 counter += custInstance.equals(null) ? 1 : 2; 179 } 180 181 if (custArrayInstance != null) { 182 if ((counter % 3) == 0) { 183 counter += (custArrayInstance instanceof String) ? 0 : 1; 184 } else { 185 counter += (custArrayInstance instanceof Object) ? 0 : 1; 186 } 187 } 188 } 189 190 return counter + s.m() + s.f + b.m() + b.f; 191 } 192 193 static void f() { 194 if (counter % 2 == 1) { 195 counter ++; 196 } 197 } 198 199 // Generated Lambda classes should be excluded from CDS preimage. 200 static void lambdaHotSpot() { 201 long start = System.currentTimeMillis(); 202 while (System.currentTimeMillis() - start < 20) { 203 doit(() -> counter ++ ); 204 } 205 } 206 207 static void doit(Runnable r) { 208 r.run(); 209 } 210 211 // All subclasses of jdk.jfr.Event are excluded from the CDS archive. 212 static class ShouldBeExcluded extends jdk.jfr.Event { 213 int f = (int)(System.currentTimeMillis()) + 123; 214 int m() { 215 return f + 456; 216 } 217 218 void hotSpot2() { 219 long start = System.currentTimeMillis(); 220 while (System.currentTimeMillis() - start < 20) { 221 for (int i = 0; i < 50000; i++) { 222 counter += i; 223 } 224 f(); 225 } 226 } 227 } 228 229 static class Bar { 230 int f = (int)(System.currentTimeMillis()) + 123; 231 int m() { 232 return f + 456; 233 } 234 235 void hotSpot3() { 236 long start = System.currentTimeMillis(); 237 while (System.currentTimeMillis() - start < 20) { 238 for (int i = 0; i < 50000; i++) { 239 counter += i; 240 } 241 f(); 242 } 243 } 244 } 245 } 246 } 247