反射是Java語言中一個相當重要的特性,它允許正在執行的Java程式觀測,甚至是修改程式的動態行為。

舉例來說,我們可以通過Class物件列舉該類中所有的方法,我們還可以通過Method.setAccessible(位於java.lang.reflect包,該方法繼承自AccessibleObject)繞過Java語言的訪問許可權,在私有方法所在類之外的地方呼叫該方法。

反射在Java中的應用十分廣泛。開發人員時常接觸到的Java整合開發環境(IDE)便運用了這一功能:每當我們敲入點號時,IDE便會根據點號前的內容,動態展示可以訪問的欄位或者方法。另一個日常應用則是Java偵錯程式,它能夠在除錯過程中列舉某一物件所有欄位的值。

該圖中,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方法的呼叫。

在生產環境中,我們往往擁有多個不同的反射呼叫,對應多個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虛擬機器的人來說是非常不錯的文章,希望大家支援一下鄭老師。