1 /* 2 * Copyright (c) 2019 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 package org.openjdk.bench.java.lang.invoke; 24 25 import org.openjdk.jmh.annotations.*; 26 import org.openjdk.jmh.runner.Runner; 27 import org.openjdk.jmh.runner.RunnerException; 28 import org.openjdk.jmh.runner.options.Options; 29 import org.openjdk.jmh.runner.options.OptionsBuilder; 30 31 import java.lang.invoke.MethodHandles; 32 import java.lang.reflect.Field; 33 import java.net.URL; 34 import java.net.URLClassLoader; 35 import java.util.concurrent.TimeUnit; 36 37 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 38 39 public class LookupDefineClass { 40 private static final byte[] X_BYTECODE = new byte[]{ 41 (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0x0A, 0x00, 42 0x03, 0x00, 0x0C, 0x07, 0x00, 0x0D, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0F, 43 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 44 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 45 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x54, 46 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 0x01, 0x00, 47 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 48 0x00, 0x08, 0x46, 0x6F, 0x6F, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 49 0x05, 0x00, 0x06, 0x01, 0x00, 0x07, 0x66, 0x6F, 0x6F, 0x2F, 0x46, 0x6F, 50 0x6F, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 51 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x12, 0x6A, 52 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x52, 0x75, 0x6E, 53 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, 54 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 55 0x06, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, 56 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, (byte)0xB7, 0x00, 0x01, (byte)0xB1, 0x00, 0x00, 57 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 58 0x00, 0x03, 0x00, 0x01, 0x00, 0x09, 0x00, 0x06, 0x00, 0x01, 0x00, 0x07, 59 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 60 (byte)0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 61 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 62 0x02, 0x00, 0x0B 63 }; 64 65 /** 66 * Our own crippled classloader, that can only load a simple class over and over again. 67 */ 68 public static class XLoader extends URLClassLoader { 69 public XLoader() { 70 super("X-Loader", new URL[0], ClassLoader.getSystemClassLoader()); 71 } 72 73 @Override 74 protected Class<?> findClass(final String name) throws ClassNotFoundException { 75 return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length); 76 } 77 78 public Class<?> install(final String name, byte[] bytes) { 79 return defineClass(name, bytes, 0, bytes.length); 80 } 81 } 82 83 public static class Loader extends URLClassLoader { 84 public Loader(String name) { 85 super(name, new URL[0], ClassLoader.getSystemClassLoader()); 86 } 87 88 public Class<?> install(final String name, byte[] bytes) { 89 return defineClass(name, bytes, 0, bytes.length); 90 } 91 } 92 93 @State(Scope.Thread) 94 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 95 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 96 @Fork(3) 97 @BenchmarkMode(Mode.AverageTime) 98 @OutputTimeUnit(TimeUnit.NANOSECONDS) 99 public static class OneClassPerLoader { 100 ClassLoader loader; 101 @Benchmark 102 public Class<?> load() throws ClassNotFoundException { 103 return Class.forName("foo.Foo", false, loader); 104 } 105 106 public static void main(String[] args) throws RunnerException { 107 Options opt = new OptionsBuilder() 108 .include(LookupDefineClass.OneClassPerLoader.class.getSimpleName()) 109 // .addProfiler(ClassloaderProfiler.class) 110 // .addProfiler(CompilerProfiler.class) 111 .build(); 112 113 new Runner(opt).run(); 114 } 115 116 @Setup(Level.Invocation) 117 public void doSetup() { 118 loader = new XLoader(); 119 } 120 } 121 122 private static final byte[] FOO_HOST_BYTES = new byte[]{ 123 (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0x00, 0x00, 0x00, 0x33, 0x00, 0x12, 0x01, 0x00, 124 0x11, 0x66, 0x6F, 0x6F, 0x2F, 0x41, 0x6E, 0x6F, 0x6E, 0x79, 0x6D, 0x6F, 125 0x75, 0x73, 0x48, 0x6F, 0x73, 0x74, 0x07, 0x00, 0x01, 0x01, 0x00, 0x10, 126 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 127 0x6A, 0x65, 0x63, 0x74, 0x07, 0x00, 0x03, 0x01, 0x00, 0x06, 0x4C, 0x4F, 128 0x4F, 0x4B, 0x55, 0x50, 0x01, 0x00, 0x27, 0x4C, 0x6A, 0x61, 0x76, 0x61, 129 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 130 0x2F, 0x4D, 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 131 0x65, 0x73, 0x24, 0x4C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, 0x3B, 0x01, 0x00, 132 0x08, 0x3C, 0x63, 0x6C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 133 0x28, 0x29, 0x56, 0x01, 0x00, 0x1E, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 134 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x2F, 0x4D, 135 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, 136 0x07, 0x00, 0x09, 0x01, 0x00, 0x06, 0x6C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, 137 0x01, 0x00, 0x29, 0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 138 0x61, 0x6E, 0x67, 0x2F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x2F, 0x4D, 139 0x65, 0x74, 0x68, 0x6F, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x73, 140 0x24, 0x4C, 0x6F, 0x6F, 0x6B, 0x75, 0x70, 0x3B, 0x0C, 0x00, 0x0B, 0x00, 141 0x0C, 0x0A, 0x00, 0x0A, 0x00, 0x0D, 0x0C, 0x00, 0x05, 0x00, 0x06, 0x09, 142 0x00, 0x02, 0x00, 0x0F, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x06, 143 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 144 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x07, 0x00, 145 0x08, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x00, 146 0x00, 0x00, 0x00, 0x00, 0x07, (byte)0xB8, 0x00, 0x0E, (byte)0xB3, 0x00, 0x10, (byte)0xB1, 147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 148 }; 149 150 private static MethodHandles.Lookup defineHostClass(Loader loader, String name, byte[] bytes) { 151 try { 152 Class<?> c = loader.install(name, bytes); 153 Field f = c.getDeclaredField("LOOKUP"); 154 return (MethodHandles.Lookup)f.get(null); 155 } catch (NoSuchFieldException|IllegalAccessException e) { 156 throw new InternalError(e); 157 } 158 } 159 160 @State(Scope.Thread) 161 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 162 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 163 @Fork(3) 164 @BenchmarkMode(Mode.AverageTime) 165 @OutputTimeUnit(TimeUnit.NANOSECONDS) 166 public static class TwoClassPerLoader { 167 XLoader loader; 168 @Benchmark 169 public Class<?> load() throws ClassNotFoundException { 170 return Class.forName("foo.Foo", false, loader); 171 } 172 173 public static void main(String[] args) throws RunnerException { 174 Options opt = new OptionsBuilder() 175 .include(LookupDefineClass.TwoClassPerLoader.class.getSimpleName()) 176 // .addProfiler(ClassloaderProfiler.class) 177 // .addProfiler(CompilerProfiler.class) 178 .build(); 179 180 new Runner(opt).run(); 181 } 182 183 @Setup(Level.Invocation) 184 public void doSetup() { 185 loader = new XLoader(); 186 loader.install("foo.AnonymousHost", FOO_HOST_BYTES); 187 } 188 } 189 190 191 @State(Scope.Thread) 192 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 193 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 194 @Fork(3) 195 @BenchmarkMode(Mode.AverageTime) 196 @OutputTimeUnit(TimeUnit.NANOSECONDS) 197 public static class WeakClass { 198 private static final MethodHandles.Lookup HOST_LOOKUP = 199 defineHostClass(new Loader("weak-class-loader"), "foo.AnonymousHost", FOO_HOST_BYTES); 200 201 @Benchmark 202 public Class<?> load() throws ClassNotFoundException { 203 try { 204 return HOST_LOOKUP.defineHiddenClass(X_BYTECODE, false).lookupClass(); 205 } catch (IllegalAccessException e) { 206 throw new InternalError(e); 207 } 208 } 209 210 public static void main(String[] args) throws RunnerException { 211 Options opt = new OptionsBuilder() 212 .include(LookupDefineClass.WeakClass.class.getSimpleName()) 213 // .addProfiler(ClassloaderProfiler.class) 214 // .addProfiler(CompilerProfiler.class) 215 .build(); 216 217 new Runner(opt).run(); 218 } 219 } 220 221 @State(Scope.Thread) 222 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 223 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 224 @Fork(3) 225 @BenchmarkMode(Mode.AverageTime) 226 @OutputTimeUnit(TimeUnit.NANOSECONDS) 227 public static class UnsafeAnonymousClass { 228 static final sun.misc.Unsafe unsafe = getUnsafe(); 229 230 private static final MethodHandles.Lookup lookup = 231 defineHostClass(new Loader("anonymous-class-loader"),"foo.AnonymousHost", FOO_HOST_BYTES); 232 233 @Benchmark 234 public Class<?> load() throws ClassNotFoundException { 235 return unsafe.defineAnonymousClass(lookup.lookupClass(), X_BYTECODE, null); 236 } 237 238 public static void main(String[] args) throws RunnerException { 239 Options opt = new OptionsBuilder() 240 .include(LookupDefineClass.UnsafeAnonymousClass.class.getSimpleName()) 241 // .addProfiler(ClassloaderProfiler.class) 242 // .addProfiler(CompilerProfiler.class) 243 .build(); 244 245 new Runner(opt).run(); 246 } 247 } 248 249 static sun.misc.Unsafe getUnsafe() { 250 try { 251 Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); 252 f.setAccessible(true); 253 return (sun.misc.Unsafe)f.get(null); 254 } catch (ReflectiveOperationException e) { 255 throw new RuntimeException(e); 256 } 257 } 258 259 @State(Scope.Thread) 260 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 261 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 262 @Fork(3) 263 @BenchmarkMode(Mode.AverageTime) 264 @OutputTimeUnit(TimeUnit.NANOSECONDS) 265 public static class HiddenClass { 266 private static final MethodHandles.Lookup HOST_LOOKUP = 267 defineHostClass(new Loader("hidden-class-loader"),"foo.AnonymousHost", FOO_HOST_BYTES); 268 269 @Benchmark 270 public Class<?> load() throws ClassNotFoundException { 271 try { 272 return HOST_LOOKUP.defineHiddenClass(X_BYTECODE, false, STRONG).lookupClass(); 273 } catch (IllegalAccessException e) { 274 throw new InternalError(e); 275 } 276 } 277 278 public static void main(String[] args) throws RunnerException { 279 Options opt = new OptionsBuilder() 280 .include(LookupDefineClass.HiddenClass.class.getSimpleName()) 281 // .addProfiler(ClassloaderProfiler.class) 282 // .addProfiler(CompilerProfiler.class) 283 .build(); 284 285 new Runner(opt).run(); 286 } 287 } 288 }