读郑雨迪《深入拆解Java虚拟机》 -- 第七讲 JVM是如何实现反射的

本文转自https://time.geekbang.org/column/article/12192

反射是Java语言中一个相当重要的特性,它允许正在运行的Java程序观测,甚至是修改程序的动态行为。

举例来说,我们可以通过Class对象枚举该类中所有的方法,我们还可以通过Method.setAccessible(位于java.lang.reflect包,该方法继承自AccessibleObject)绕过Java语言的访问权限,在私有方法所在类之外的地方调用该方法。

反射在Java中的应用十分广泛。开发人员时常接触到的Java集成开发环境(IDE)便运用了这一功能:每当我们敲入点号时,IDE便会根据点号前的内容,动态展示可以访问的字段或者方法。另一个日常应用则是Java调试器,它能够在调试过程中枚举某一对象所有字段的值。

《读郑雨迪《深入拆解Java虚拟机》 -- 第七讲 JVM是如何实现反射的》该图中,eclipse的自动提示使用了反射

在Web开发中,我们经常能够接触到各种可配置的通用框架。为了保证框架的可扩展性,它们往往借助Java的反射机制。举例来说,Spring框架的依赖反转(IoC),便是依赖于反射机制。

然而,我相信不少开发人员都嫌弃反射机制比较慢。甚至是甲骨文关于反射的教学网页,也强调了反射性能开销大的缺点。

如果你对反射API不是特别熟悉的话,请先查看Java反射API简介

反射调用的实现

首先,我们来看看方法的反射调用,也就是Method.invoke,是怎么实现的。

public final class Method extends Executable{
    ...
    public Object invoke(Object obj, Object... args) throws ...{
        ... // 权限检查
        MethodAccessor ma = methodAccessor;
        if(ma == null){
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
}

通过查阅Method.invoke的源代码,我们发现,它实际上委派给MethodAccessor来处理。MethodAccessor是一个接口,它有两种已有的具体实现:一个通过本地方法来实现反射调用,另一个这是用了委派模式。为了方便记忆,下文使用“本地实现”和“委派实现”来指代两者。

每个Method实例的第一次反射调用都会生成一个委派实现,它所委派的具体实现便是一个本地实现。本地实现非常容易理解。当进入了Java虚拟机内部之后,我们便有了Method实例锁指方向的具体地址。这时候,反射调用无非就是将传入参数准备好,然后调用进入目标方法。

// v0 版本
import java.lang.reflect.Method;

public class Test {
    public static void target(int i) {
        new Exception("#" + i).printStackTrace();
    }

    public static void main(String[] args){
        Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        method.invoke(null, 0);
    }
}

得到输出

java.lang.Exception: #0
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:19)

为了方便理解,我么可以打印一下反射调用到目标方法时的栈轨迹。在上面的v0版本代码中,我们获取了一个指向Test.target方法的Method对象,并且通过它来进行反射调用。在Test.target中,打印出栈轨迹。

可以看到,反射调用先是调用了Method.invoke,然后进入委派实现(DelegatingMethodAccessorImpl),再然后进入本地实现(NativeMethodAccessorImpl),最后到达目标方法

这里我们好奇,为什么反射调用还要采取委派实现作为中间层?直接交给本地实现不可以么?

其实,Java的反射调用机制还设立了另一种动态生成字节码的实现(下文称为动态实现),直接使用invoke指令来调用目标方法。之所以采用委派实现,便是为了能够在本地实现以及动态实现中切换

//动态实现的伪代码, 这里只列举关键的调用逻辑,其实它还包括调用者检测、参数检测的字节码
package jdk.internal.reflect;

public class GeneratedMethodAccessor1 extends ... {
    @Override
    public Object invoke(Object obj, Object[] args){
        Test.target((int) args[0]);
        return null;
    }
}

动态实现和本地实现相比,其运行效率要快上20倍。这是因为动态实现无需经过Java到C++再到Java的切换,但由于生成字节码十分耗时,仅调用一次的话,反而是本地实现要快上3到4倍

考虑到许多反射调用仅会执行一次,Java虚拟机设置了一个阈值15(可以通过-Dsun,reflect.inflationThreshold=来调整),当某个反射调用的调用次数在15以下时,采用本地实现;当达到15时,便开始动态生成字节码,并将委派实现的委派对象切换至动态实现,这个过程我们称之为inflation。

为了观察这个过程,我们将刚才的例子更改为下面的v1版本。它会将反射调用循环20次。

//v1 版本

import java.lang.reflect.Method;

public class Test {
	public static void target(int i) {
        new Exception("#" + i).printStackTrace();
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        for(int i = 0; i < 20;i++){
        	method.invoke(null, i);
        }
        
	}

}

使用 -verbose:class打印加载的类

java -verbose:class Test
[Opened D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Object from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.Serializable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Comparable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.CharSequence from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.String from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Type from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Cloneable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.System from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Throwable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Error from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ThreadDeath from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Exception from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.RuntimeException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.SecurityManager from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.ProtectionDomain from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.AccessControlContext from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.SecureClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ReflectiveOperationException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassNotFoundException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.LinkageError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.NoClassDefFoundError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassCastException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ArrayStoreException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.VirtualMachineError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.OutOfMemoryError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StackOverflowError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.IllegalMonitorStateException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Reference from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.SoftReference from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.WeakReference from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.FinalReference from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.PhantomReference from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Cleaner from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Finalizer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Runnable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Thread from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Thread$UncaughtExceptionHandler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ThreadGroup from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Map from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Dictionary from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Hashtable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Properties from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.AccessibleObject from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Member from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Field from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Parameter from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Executable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Method from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Constructor from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.MagicAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.MethodAccessor from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ConstructorAccessor from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ConstructorAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.DelegatingClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ConstantPool from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.FieldAccessor from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.FieldAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.UnsafeFieldAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.UnsafeStaticFieldAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.annotation.Annotation from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.CallerSensitive from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandle from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.DirectMethodHandle from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MemberName from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleNatives from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.LambdaForm from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodType from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.BootstrapMethodError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.CallSite from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.ConstantCallSite from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MutableCallSite from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.VolatileCallSite from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Appendable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.AbstractStringBuilder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StringBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StringBuilder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Unsafe from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.AutoCloseable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.Closeable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.InputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.ByteArrayInputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.File from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URL from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.jar.Manifest from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher$AppClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher$ExtClassLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.CodeSource from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StackTraceElement from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.Buffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Boolean from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Character from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Number from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Float from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Double from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Byte from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Short from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Integer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Long from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.NullPointerException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ArithmeticException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.ObjectStreamField from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Comparator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.String$CaseInsensitiveComparator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.Guard from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.Permission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.BasicPermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.RuntimePermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.AccessController from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.ReflectPermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.PrivilegedAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ReflectionFactory$GetReflectionFactoryAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.cert.Certificate from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Iterable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.List from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.RandomAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.AbstractCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.AbstractList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Vector from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Stack from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ReflectionFactory from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Reference$Lock from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Reference$ReferenceHandler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.InterruptedException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.ArrayList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Set from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.AbstractSet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$EmptySet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$EmptyList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.AbstractMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$EmptyMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableRandomAccessList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.ReferenceQueue from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.ReferenceQueue$Null from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.ReferenceQueue$Lock from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaLangRefAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Reference$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.SharedSecrets from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.IncompatibleClassChangeError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.NoSuchMethodError from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.Reflection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.HashMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Map$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.HashMap$Node from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.VM from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Hashtable$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Math from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ref.Finalizer$FinalizerThread from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.Charset from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.spi.CharsetProvider from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.FastCharsetProvider from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StandardCharsets from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.PreHashedMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StandardCharsets$Aliases from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StandardCharsets$Classes from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StandardCharsets$Cache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ThreadLocal from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicInteger from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class$3 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class$ReflectionData from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class$Atomic from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.generics.repository.AbstractRepository from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.generics.repository.GenericDeclRepository from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.generics.repository.ClassRepository from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class$AnnotationData from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.annotation.AnnotationType from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.WeakHashMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassValue$ClassValueMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Modifier from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.LangReflectAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.ReflectAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Arrays from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.Charset$ExtendedProviderHolder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.Charset$ExtendedProviderHolder$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Opened D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded sun.nio.cs.AbstractCharsetProvider from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ext.ExtendedCharsets from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded java.lang.Class$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ReflectionFactory$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.NativeConstructorAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.DelegatingConstructorAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.SortedMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.NavigableMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.TreeMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.ASCIICaseInsensitiveComparator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.TreeMap$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.HistoricallyNamedCharset from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ext.GBK from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded java.lang.StringCoding from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ThreadLocal$ThreadLocalMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ThreadLocal$ThreadLocalMap$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StringCoding$StringDecoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ext.DoubleByte from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded sun.nio.cs.ext.DelegatableDecoder from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded sun.nio.cs.ArrayDecoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CharsetDecoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ext.DoubleByte$Decoder from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded java.nio.charset.CodingErrorAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Hashtable$EntrySet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$SynchronizedCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$SynchronizedSet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Objects from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Enumeration from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Iterator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Hashtable$Enumerator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Runtime from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Version from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileInputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileDescriptor from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaIOFileDescriptorAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileDescriptor$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.Flushable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.OutputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileOutputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FilterInputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.BufferedInputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicReferenceFieldUpdater from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.PrivilegedExceptionAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.misc.ReflectUtil from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FilterOutputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.PrintStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.BufferedOutputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.Writer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.OutputStreamWriter from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StreamEncoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.security.action.GetPropertyAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.Unicode from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_8 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ArrayEncoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CharsetEncoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_8$Encoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.ByteBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.HeapByteBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.Bits from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.ByteOrder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicLong from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaNioAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.Bits$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.BufferedWriter from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.DefaultFileSystem from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileSystem from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.WinNTFileSystem from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.ExpiringCache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.LinkedHashMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.ExpiringCache$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Enum from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.File$PathStatus from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.file.Watchable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.file.Path from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.StringCoding$StringEncoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ext.DoubleByte$Encoder from D:\java\jre1.8.0_152\lib\charsets.jar]
[Loaded java.lang.Readable from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.CharBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.HeapCharBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CoderResult from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CoderResult$Cache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CoderResult$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.CoderResult$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassLoader$3 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.ExpiringCache$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.LinkedHashMap$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassLoader$NativeLibrary from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Terminator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.SignalHandler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Terminator$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Signal from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.NativeSignalHandler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Integer$IntegerCache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.OSEnvironment from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.io.Win32ErrorMode from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaLangAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.System$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.IllegalArgumentException from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Compiler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Compiler$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLStreamHandlerFactory from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher$Factory from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.security.util.Debug from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassLoader$ParallelLoaders from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.WeakHashMap$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Collections$SetFromMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.WeakHashMap$KeySet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaNetAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLClassLoader$7 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.StringTokenizer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher$ExtClassLoader$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.MetaIndex from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.Reader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.BufferedReader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.InputStreamReader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileReader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.StreamDecoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_8$Decoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.reflect.Array from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Locale from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.LocaleObjectCache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Locale$Cache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.locks.Lock from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.locks.ReentrantLock from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$Segment from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$Node from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$CounterCell from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$CollectionView from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$KeySetView from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$ValuesView from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.ConcurrentHashMap$EntrySetView from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.BaseLocale from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.BaseLocale$Cache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.BaseLocale$Key from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.LocaleObjectCache$CacheEntry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Locale$LocaleKey from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.util.locale.LocaleUtils from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.CharacterData from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.CharacterDataLatin1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.HashMap$TreeNode from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileInputStream$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.ParseUtil from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.BitSet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.Parts from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLStreamHandler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.protocol.file.Handler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaSecurityAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.ProtectionDomain$JavaSecurityAccessImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.ProtectionDomain$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.ProtectionDomain$Key from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.Principal from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.HashSet from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.protocol.jar.Handler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Launcher$AppClassLoader$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.SystemClassLoaderAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleImpl$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.function.Function from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleImpl$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleImpl$3 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassValue from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleImpl$4 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassValue$Entry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassValue$Identity from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.ClassValue$Version from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MemberName$Factory from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleStatics from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.invoke.MethodHandleStatics$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.PostVMInitHook from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.usagetracker.UsageTrackerClient from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.concurrent.atomic.AtomicBoolean from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.usagetracker.UsageTrackerClient$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.usagetracker.UsageTrackerClient$4 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.usagetracker.UsageTrackerClient$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded jdk.internal.util.EnvUtils from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.usagetracker.UsageTrackerClient$3 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FileOutputStream$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.launcher.LauncherHelper from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLClassLoader$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.util.URLUtil from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$3 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$Loader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$JarLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipConstants from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipFile from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaUtilZipFileAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipFile$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$JarLoader$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.FileURLMapper from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.jar.JarFile from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JavaUtilJarAccess from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.jar.JavaUtilJarAccessImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.charset.StandardCharsets from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.US_ASCII from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ISO_8859_1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_16BE from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_16LE from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.UTF_16 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Queue from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.Deque from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.ArrayDeque from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipCoder from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.PerfCounter from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Perf$GetPerfAction from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Perf from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.PerfCounter$CoreCounters from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.ch.DirectBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.MappedByteBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.DirectByteBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.LongBuffer from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.nio.DirectLongBufferU from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.JarIndex from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipEntry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.jar.JarEntry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.jar.JarFile$JarFileEntry from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.zip.ZipFile$ZipFileInputStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.AbstractSequentialList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.LinkedList from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.LinkedList$Node from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$FileLoader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ThreadLocalCoders from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ThreadLocalCoders$Cache from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ThreadLocalCoders$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.cs.ThreadLocalCoders$2 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.Resource from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.misc.URLClassPath$FileLoader$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Package from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.nio.ByteBuffered from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.PermissionCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.Permissions from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.net.URLConnection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.URLConnection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.protocol.file.FileURLConnection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.net.www.MessageHeader from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FilePermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FilePermission$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.io.FilePermissionCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.AllPermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.UnresolvedPermission from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.security.BasicPermissionCollection from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded Test from file:/I:/%e9%a1%b9%e7%9b%ae/reFly/WebRoot/WEB-INF/classes/]
[Loaded sun.launcher.LauncherHelper$FXHelper from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Class$MethodArray from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Void from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.NativeMethodAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Throwable$PrintStreamOrWriter from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Throwable$WrappedPrintStream from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.IdentityHashMap from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.IdentityHashMap$KeySet from D:\java\jre1.8.0_152\lib\rt.jar]
java.lang.Exception: #0
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #1
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #2
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #3
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #4
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #5
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #6
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #7
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #8
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #9
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #10
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #11
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #12
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #13
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #14
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
[Loaded sun.reflect.ClassFileConstants from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.AccessorGenerator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ByteVectorFactory from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ByteVector from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ByteVectorImpl from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ClassFileAssembler from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.UTF8 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.Label from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.Label$PatchInfo from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.util.ArrayList$Itr from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner$1 from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]
java.lang.Exception: #15
	at Test.target(Test.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
[Loaded java.util.concurrent.ConcurrentHashMap$ForwardingNode from D:\java\jre1.8.0_152\lib\rt.jar]
java.lang.Exception: #16
	at Test.target(Test.java:13)
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #17
	at Test.target(Test.java:13)
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #18
	at Test.target(Test.java:13)
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
java.lang.Exception: #19
	at Test.target(Test.java:13)
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at Test.main(Test.java:20)
[Loaded java.lang.Shutdown from D:\java\jre1.8.0_152\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from D:\java\jre1.8.0_152\lib\rt.jar]

可以看到,在第15次(从0开始数)反射调用时,我们便触发了动态实现的生成。这时候,Java虚拟机额外加载了不少类。其中最重要的当属GeneratedMethodAccessor1。并且,从第16尺反射调用开始,我们便切换至这个刚刚生成的动态实现。

反射调用的inflation机制是可以通过参数(-Dsun.reflect.noInflation=true)来关闭的。这样一来,在反射调用一开始便会直接生成动态实现,而不是委派实现或者本地实现。

反射调用的开销

我们来拆解反射调用的性能开销。

在刚才的例子中,我们先后进行了Class.forName, Class.getMethod以及Method.invoke三个操作。其中Class.forName会调用本地方法,Class.getMethod则会遍历该类的共有方法。如果没有匹配到,它还将遍历父类的公有方法。可想而知,这两个操作都非常费时。

值得注意的是,以getMethod为代表的查找方法操作,会返回查找得到结果的一份拷贝。因此,我们应当避免在热点代码中使用返回Method数组的getMethods 或者 getDeclaredMethods方法,以减少不必要的堆空间消耗。。

在实践中,我们往往会在应用程序中缓存Class.forName 和 Class.getMethod的结果。因此, 下面我们就只关注反射调用本身的性能开销。

为了比较直接调用和反射调用的性能差距,我们将前面的例子改为下面的v2版本。它将会反射调用循环二十亿次。此外,它还将记录下没跑一亿次的时间。

我们取最后五个记录的平均值,作为预热后的峰值性能。(注:这种性能评估并不严谨)

在笔者的老笔记本上,一亿次直接调用耗费的时间大约在260ms。这和不调用的时间是一致的。其原因在于这段代码属于热循环,同样会触发即时编译。并且,即时编译会将对Test.target的调用内联进来,从而消除了调用的开销。

import java.lang.reflect.Method;

public class Test {
	public static void target(int i) {
		// 空方法
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        
        long current = System.currentTimeMillis();
        for(int i = 1; i <= 2000000000;i++){
        	if(i % 100000000 == 0){
        		long temp = System.currentTimeMillis();
        		System.out.println(temp - current);
        		current = temp;
        	}
        	
        	method.invoke(null, 128);
//        	target(128);
        }
        
	}

}

下面我们以260ms作为基准,来比较反射调用的性能开销。

由于目标方法Test.target接受一个int类型的参数,因此我们传入128作为反射调用的参数,测得结果约为(700ms)基准的2.7倍。我们暂且不管这个数字是高是低,先来看看在反射调用之前字节码都做了什么。

   59: aload_2                         // 加载 Method 对象
   60: aconst_null                     // 反射调用的第一个参数 null
   61: iconst_1
   62: anewarray Object                // 生成一个长度为 1 的 Object 数组
   65: dup
   66: iconst_0
   67: sipush 128
   70: invokestatic Integer.valueOf    // 将 128 自动装箱成 Integer
   73: aastore                         // 存入 Object 数组中
   74: invokevirtual Method.invoke     // 反射调用

这里截取了循环中反射调用编译而成的字节码。可以看到,这段字节码除了反射调用外,还额外做了两个操作。

  • 由于Method.invoke是一个变长参数方法,在字节码层面它的最后一个参数会是Object数组。Java编译器会在方法调用处生成一个长度为传入参数数量的Object数组, 并将传入参数一一存储进该数组中。
  • 由于Object数组不能存储基本类型,Java编译器会对传入的基本类型参数进行自动装箱。

这两个操作除了带来额外的性能开销外,还可能占用堆内存,使得GC更加频繁。(有时间的小伙伴可以用虚拟机参数 -XX:_PrintGC试试。)那么,如何消除这部分开销呢?

关于自动装箱,Java缓存了[-128, 127]中所有整数对应的Integer对象。当需要自动装箱的整数在这个范围之内时,便返回缓存的Integer,否则需要新建一个Integer对象。

因此,我们可以将这个缓存的返回扩大至覆盖128(对应参数-Djava.lang.Integer.IntegerCache.high=128),便可以避免需要新建Integer对象的场景。或者,我们可以在循环外缓存128自动装箱得到的Integer对象,并且直接传入反射调用中。这两种方法测得的结果(469ms和460ms)差不多,约为基础的1.8倍。

现在再看看第一个因变长参数而自动生成的Object数组。既然每个反射调用对应的参数个数是固定的,那么我们可以选择在循环外新建一个Object数组,设置好参数,并直接交给反射调用。改好的代码可以得到我们的v3版本。

// v3 版本

import java.lang.reflect.Method;


public class Test {
	public static void target(int i) {
		// 空方法
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        Object[] arg = new Object[1]; // 在循环体外构造参数数组
        arg[0] = 128;
        
        long current = System.currentTimeMillis();
        for(int i = 1; i <= 2000000000;i++){
        	if(i % 100000000 == 0){
        		long temp = System.currentTimeMillis();
        		System.out.println(temp - current);
        		current = temp;
        	}
        	
        	method.invoke(null, arg);
//        	target(128);
        }
        
	}

}

测得的结果(652ms)反而更糟糕了,为基准的2.5倍。

如果在上一步解决了自动装箱之后查看运行时的GC状况,我们会发现这段程序并不会触发GC,其原因在于,原本的反射调用被内联了,从而使得即时编译器中的逃逸分析将原本新建的Object数组判定为不逃逸的对象

如果一个对象不逃逸,那么即时编译器可以选择栈分配甚至是虚拟分配,也就是不占用堆空间

如果在循环外新建数组,计时编译器无法确定这个数组会不会被中途修改,因此无法优化掉访问数组的操作,可谓是得不偿失。

前面橙提到,可以关闭反射调用的Inflation 机制,从而取消委派实现,并且直接使用动态实现。此外,每次反射调用都会检查目标方法的权限,而这个检查同样可以在Java代码里关闭,在关闭了这两项机制之后,也就得到了我们的v4版本,它测得的结果(311ms)约为基准的1.2倍。

// v4 版本

import java.lang.reflect.Method;

// 在运行指令中添加如下两个虚拟机参数:
// -Djava.lang.Integer.IntegerCache.high=128
// -Dsun.reflect.noInflation=true
public class Test {
	public static void target(int i) {
		// 空方法
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        method.setAccessible(true); // 关闭权限检查
        
        long current = System.currentTimeMillis();
        for(int i = 1; i <= 2000000000;i++){
        	if(i % 100000000 == 0){
        		long temp = System.currentTimeMillis();
        		System.out.println(temp - current);
        		current = temp;
        	}
        	
        	method.invoke(null, 128);
//        	target(128);
        }
        
	}

}

到这里,我们基本上把反射调用的水分都榨干了。接下来,我们来把反射调用的性能开销给提回去。

首先,在这个例子中,之所以反射调用能够变得这么快,主要是因为即时编译器中的方法内联。在关闭了Inflation的情况下,内联额瓶颈在于Method.invoke方法中对MethodAccessor.invoke方法的调用。

《读郑雨迪《深入拆解Java虚拟机》 -- 第七讲 JVM是如何实现反射的》

在生产环境中,我们往往拥有多个不同的反射调用,对应多个GeneratedMethodAccessor, 也就是动态实现

由于Java虚拟机的关旭上述调用点的类型profle(注:invokevirtual 或者 invokeinterface,Java虚拟机会记录下调用者的具体类型, 我们称之为类型profile)无法同时记录多个类,因此可能造成所测试的反射调用没有被内联的情况。

// v5 版本

import java.lang.reflect.Method;


public class Test {
	public static void target(int i) {
		// 空方法
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        method.setAccessible(true); // 关闭权限检查
        polluteProfile();
        
        long current = System.currentTimeMillis();
        for(int i = 1; i <= 2000000000;i++){
        	if(i % 100000000 == 0){
        		long temp = System.currentTimeMillis();
        		System.out.println(temp - current);
        		current = temp;
        	}
        	
        	method.invoke(null, 128);
//        	target(128);
        }
        
	}
	
	public static void polluteProfile() throws Exception {
		Method method1 = Test.class.getMethod("target1", int.class);
		Method method2 = Test.class.getMethod("target2", int.class);
		
		for(int i = 0;i < 2000; i++){
			method1.invoke(null, 0);
			method2.invoke(null, 0);
		}
	}

	public static void target1(int i) { }
	
	public static void target2(int i) { }
}

在上述v5版本中,我们在测试宣外之前调用了polluteProfile的方法。该方法将反射调用另外两个方法,并且循环上2000遍。

而测试循环保持不变,测得的结果(1500ms)约为基准的5.7倍。也就是说,只要误扰了Method.invoke方法的类型profile,性能开销便会从1.3倍上升至5.7倍。

之所以这么慢,除了没有内联之外,另外一个原因是逃逸分析不再起效。这时候,我们便可以采用刚才的v3版本中的解决方案,在循环外构造参数数组,并直接传递给反射调用。这样子测得的结果(1013ms)约为基准的3.9倍。

除此之外。我们还可以提高Java虚拟机关于每个调用能够记录的类型数目(对应虚拟机参数 -XX:TypeProfileWidth,默认值为2,这里设置为3)。最终测得的结果(730ms)约为基准的2.8倍,尽管它和原本的1.2倍还有一定的差距,但总算比5.7倍好多了。

实践环节

我们将最后一段代码中pulluteProfle方法的两个Method对象,都改成获取名字为“target”的方法。这两个获得的Method对象是同一个吗?他们equal吗?对我们的运行结果有什么影响

import java.lang.reflect.Method;

public class Test {
	public static void target(int i) {
		// 空方法
    }

	public static void main(String[] args) throws Exception{
		Class<?> klass = Class.forName("Test");
        Method method = klass.getMethod("target", int.class);
        method.setAccessible(true); // 关闭权限检查
        polluteProfile();
//        Object[] arg = new Object[1];
//        arg[0] = 128;
        
        long current = System.currentTimeMillis();
        for(int i = 1; i <= 2000000000;i++){
        	if(i % 100000000 == 0){
        		long temp = System.currentTimeMillis();
        		System.out.println(temp - current);
        		current = temp;
        	}
        	
        	method.invoke(null, 128);
//        	target(128);
        }
        
	}
	
	public static void polluteProfile() throws Exception {
		Method method1 = Test.class.getMethod("target", int.class);
		Method method2 = Test.class.getMethod("target", int.class);
		
		for(int i = 0;i < 2000; i++){
			method1.invoke(null, 0);
			method2.invoke(null, 0);
		}
	}

	public static void target1(int i) { }
	
	public static void target2(int i) { }
}

显然,我们是不同的引用,但它们指向的值是相等的,即method1==method2 为false,method1.equals(method2)为true。结果就是又会恢复到以前的运行速率,因为类型profile不会被target1和target2占用了。

此文从极客时间专栏《深入理解Java虚拟机》搬运而来,撰写此文的目的:

  1. 对自己的学习总结归纳

  2. 此篇文章对想深入理解Java虚拟机的人来说是非常不错的文章,希望大家支持一下郑老师。

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/ti_an_di/article/details/82049230
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞