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, WEAK);
 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);
 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 }