最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java反射及性能詳細

 更新時間:2021年10月11日 17:17:40   作者:晟_1044290  
這篇文章主要介紹了Java反射及性能,現(xiàn)如今的java工程中,反射的使用無處無在。無論是設(shè)計模式中的代理模式,還是紅透半邊天的Spring框架中的IOC,AOP等等,都存在大量反射的影子。下面我們就對該話題進行詳細介紹,感興趣的小伙伴可以參考一下

我們今天不探討框架層面的內(nèi)容,暫且認為90%的框架不存在無法容忍的性能問題。在做系統(tǒng)調(diào)優(yōu)的過程中,面對隨處可見的invoke調(diào)用,我的內(nèi)心其實是比較抵觸的,倒不是說反射怎么不好,對于優(yōu)雅的源碼來說,反射必不可少,個人抵觸的原因主要是因為反射把真實的方法“隱藏”的很好,面對長長的線程棧比較頭大而已。而且我心里一直有個大大的問號,反射到底存在哪些性能問題。

帶著這個疑惑,基于java最基本的反射使用,通過查看資料及源碼閱讀,有如下的總結(jié)和分享,歡迎交流和指正。

一、準備

注:本案例針對JDK1.8

測試代碼:

【TestRef.java】
public class TestRef {
 
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.allen.commons.entity.CommonTestEntity");
            Object refTest = clazz.newInstance();
            Method method = clazz.getMethod("defaultMethod");
            //Method method1 = clazz.getDeclaredMethod("defaultMethod");
            method.invoke(refTest);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
 
    }
}
---------------------------------------------------------------------------------------
【CommonTestEntity.java】
public class CommonTestEntity {
 
    static {
        System.out.println("CommonTestEntity執(zhí)行類加載...");
    }
 
    public CommonTestEntity() {
        System.out.println(this.getClass() + " | CommonTestEntity實例初始化 | " + this.getClass().getClassLoader());
    }
 
    public void defaultMethod() {
        System.out.println("執(zhí)行實例方法:defaultMethod");
    }
}

二、反射調(diào)用流程

1.反射的使用

  • 1)創(chuàng)建class對象(類加載,使用當前方法所在類的ClassLoader來加載)
  • 2)獲取Method對象(getMethod getDeclaredMethod
  • 3)調(diào)用invoke方法

2.getMethod 和 getDeclaredMethod區(qū)別

getMethod源碼如下:

public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        Objects.requireNonNull(name);
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // 1. 檢查方法權(quán)限
            checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
        }
        // 2. 獲取方法
        Method method = getMethod0(name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(methodToString(name, parameterTypes));
        }
        // 3. 返回方法
        return method;
    }
---------------------------------------------------------------------------------------
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        Objects.requireNonNull(name);
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // 1. 檢查方法是權(quán)限
            checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
        }
        // 2. 獲取方法
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(methodToString(name, parameterTypes));
        }
        // 3. 返回方法
        return method;
}

獲取方法的流程分三步走:

  • a.檢查方法權(quán)限
  • b.獲取方法 Method 對象
  • c.返回方法

主要有兩個區(qū)別:

1.getMethod checkMemberAccess 傳入的是 Member.PUBLIC,而 getDeclaredMethod 傳入的是 Member.DECLARED 。

代碼中的注釋:

注釋里解釋了 PUBLIC DECLARED 的不同,PUBLIC 會包括所有的 public 方法,包括父類的方法,而 DECLARED 會包括所有自己定義的方法,public,protected,private 都在此,但是不包括父類的方法。

2.getMethod 中獲取方法調(diào)用的是 getMethod0,而 getDeclaredMethod 獲取方法調(diào)用的是 privateGetDeclaredMethods 。privateGetDeclaredMethods 是獲取類自身定義的方法,參數(shù)是 boolean publicOnly,表示是否只獲取公共方法。

privateGetDeclaredMethods 源碼如下:

// Returns an array of "root" methods. These Method objects must NOT
    // be propagated to the outside world, but must instead be copied
    // via ReflectionFactory.copyMethod.
    private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else {
                rd.declaredMethods = res;
            }
        }
        return res;
    }

relectionData 通過緩存獲取

②如果緩存沒有命中的話,通過 getDeclaredMethods0 獲取方法

getMethod0源碼如下:

private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
        MethodArray interfaceCandidates = new MethodArray(2);
        Method res =  privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
        if (res != null)
            return res;
 
        // Not found on class or superclass directly
        interfaceCandidates.removeLessSpecifics();
        return interfaceCandidates.getFirst(); // may be null
    }

其中privateGetMethodRecursive方法中也會調(diào)用到privateGetDeclaredMethods方法和searchMethods方法

3.getMethod 方法流程

圖片2.png

4.getDeclaredMethod方法流程

圖片3.png

三、調(diào)用反射方法

invoke源碼:

class Method {
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            // 1. 檢查權(quán)限
            checkAccess(caller, clazz,
                        Modifier.isStatic(modifiers) ? null : obj.getClass(),
                        modifiers);
        }
        // 2. 獲取 MethodAccessor
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            // 創(chuàng)建 MethodAccessor
            ma = acquireMethodAccessor();
        }
        // 3. 調(diào)用 MethodAccessor.invoke
        return ma.invoke(obj, args);
    }
}

Method.invoke()實際上并不是自己實現(xiàn)的反射調(diào)用邏輯,而是委托給sun.reflect.MethodAccessor來處理。

每個實際的Java方法只有一個對應(yīng)的Method對象作為root(實質(zhì)上就是Method類的一個成員變量)。每次在通過反射獲取Method對象時新創(chuàng)建Method對象把root封裝起來。在第一次調(diào)用一個實際Java方法對應(yīng)得Method對象的invoke()方法之前,實現(xiàn)調(diào)用邏輯的MethodAccessor對象是第一次調(diào)用時才會新建并更新給root,然后調(diào)用MethodAccessor.invoke()真正完成反射調(diào)用。

MethodAccessor只是單方法接口,其invoke()方法與Method.invoke()的對應(yīng)。創(chuàng)建MethodAccessor實例的是ReflectionFactory。

MethodAccessor實現(xiàn)有兩個版本,一個是Java實現(xiàn)的,另一個是native code實現(xiàn)的。

Java 版本的 MethodAccessorImpl 調(diào)用效率比 Native 版本要快 20 倍以上,但是 Java 版本加載時要比 Native 多消耗 3-4 倍資源,所以默認會調(diào)用 Native 版本,如果調(diào)用次數(shù)超過 15 次以后,就會選擇運行效率更高的 Java 版本。

Native版本中的閾值(靜態(tài)常量)

圖片4.png

四、反射效率低的原因

1.Method#invoke 方法會對參數(shù)做封裝和解封操作

我們可以看到,invoke 方法的參數(shù)是 Object[] 類型,也就是說,如果方法參數(shù)是簡單類型(8中基本數(shù)據(jù)類型)的話,需要在此轉(zhuǎn)化成 Object 類型,例如 long ,在 javac compile 的時候 用了Long.valueOf() 轉(zhuǎn)型,也就大量了生成了Long 的 Object, 同時 傳入的參數(shù)是Object[]數(shù)值,那還需要額外封裝object數(shù)組。

而在上面 MethodAccessorGenerator#emitInvoke 方法里我們看到,生成的字節(jié)碼時,會把參數(shù)數(shù)組拆解開來,把參數(shù)恢復(fù)到?jīng)]有被 Object[] 包裝前的樣子,同時還要對參數(shù)做校驗,這里就涉及到了解封操作。

因此,在反射調(diào)用的時候,因為封裝和解封,產(chǎn)生了額外的不必要的內(nèi)存浪費,當調(diào)用次數(shù)達到一定量的時候,還會導(dǎo)致 GC。

2.需要檢查方法可見性

checkAccess方法

3.需要遍歷方法并校驗參數(shù)

PrivateGetMethodRecursive中的searhMethod

4.JIT 無法優(yōu)化

在 JavaDoc 中提到:

Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.

五、反射優(yōu)化

1.(網(wǎng)上看到)盡量不要getMethods()后再遍歷篩選,而直接用getMethod(methodName)來根據(jù)方法名獲取方法

但是在源碼中獲取方法的時候,在searchMethods方法中,其實也是采用遍歷所有方法的方式。但是相比getMethod,getDeclaredMethod遍歷的方法數(shù)量相對較少,因為不包含父類的方法。

2.緩存class對象

a)Class.forName性能比較差

b)如上所述,在獲取具體方法時,每次都要調(diào)用native方法獲取方法列表并遍歷列表,判斷入?yún)㈩愋秃头祷仡愋汀⒎瓷涞玫降?code>method/field/constructor對象做緩存,將極大的提高性能。

3.涉及動態(tài)代理的:在實際使用中,CGLIB和Javassist基于動態(tài)代碼的代理實現(xiàn),性能要優(yōu)于JDK自帶的動態(tài)代理

JDK自帶的動態(tài)代理是基于接口的動態(tài)代理,相比較直接的反射操作,性能還是高很多,因為接口實例相關(guān)元數(shù)據(jù)在靜態(tài)代碼塊中創(chuàng)建并且已經(jīng)緩存在類成員屬性中,在運行期間是直接調(diào)用,沒有額外的反射開銷。

4.使用ReflectASM,通過生成字節(jié)碼的方式加快反射(使用難度大)

到此這篇關(guān)于Java反射及性能詳細的文章就介紹到這了,更多相關(guān)Java反射及性能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 超詳細講解SpringBoot參數(shù)校驗實例

    超詳細講解SpringBoot參數(shù)校驗實例

    經(jīng)常需要提供接口與用戶交互(獲取數(shù)據(jù)、上傳數(shù)據(jù)等),由于這個過程需要用戶進行相關(guān)的操作,為了避免出現(xiàn)一些錯誤的數(shù)據(jù)等,一般需要對數(shù)據(jù)進行校驗,下面這篇文章主要給大家介紹了關(guān)于SpringBoot各種參數(shù)校驗的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • Redis實現(xiàn)商品秒殺功能頁面流程

    Redis實現(xiàn)商品秒殺功能頁面流程

    這篇文章主要介紹了Redis實現(xiàn)商品秒殺功能的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-09-09
  • 解析Java和IDEA中的文件打包問題

    解析Java和IDEA中的文件打包問題

    這篇文章主要介紹了Java和IDEA中的文件打包問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-07-07
  • idea新建文件后文件夾消失的問題及解決

    idea新建文件后文件夾消失的問題及解決

    這篇文章主要介紹了idea新建文件后文件夾消失的問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springboot跨域CORS處理代碼解析

    springboot跨域CORS處理代碼解析

    這篇文章主要介紹了springboot跨域CORS處理代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • 搭建MyBatis開發(fā)環(huán)境及基本的CURD介紹

    搭建MyBatis開發(fā)環(huán)境及基本的CURD介紹

    這篇文章主要介紹了搭建MyBatis開發(fā)環(huán)境及基本的CURD,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • 詳解Java中List接口底層實現(xiàn)原理

    詳解Java中List接口底層實現(xiàn)原理

    Java是一種廣泛應(yīng)用的編程語言,被廣泛應(yīng)用于各種平臺和應(yīng)用領(lǐng)域,List接口是Java中最重要的數(shù)據(jù)結(jié)構(gòu)之一,它為我們提供了一種靈活、高效、可擴展的數(shù)據(jù)結(jié)構(gòu),本篇文章將首先介紹Java中List接口的基本特性和使用方法,然后深入研究List接口的底層實現(xiàn)原理
    2023-11-11
  • Java Management Extensions管理擴展原理解析

    Java Management Extensions管理擴展原理解析

    這篇文章主要介紹了Java Management Extensions管理擴展原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • java高級應(yīng)用:線程池的全面講解(干貨)

    java高級應(yīng)用:線程池的全面講解(干貨)

    這篇文章主要介紹了java高級應(yīng)用:線程池的全面講解(干貨),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java中的OkHttpClient 工具類

    Java中的OkHttpClient 工具類

    這篇文章主要介紹了Java中的OkHttpClient工具類,具有很好的參考價值,希望杜大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論

保定市| 南川市| 襄城县| 合肥市| 攀枝花市| 连州市| 马公市| 蒙山县| 马尔康县| 酒泉市| 安平县| 昂仁县| 旬邑县| 股票| 临漳县| 河东区| 新丰县| 汨罗市| 丹棱县| 布拖县| 涟源市| 通州市| 伊吾县| 新闻| 格尔木市| 稻城县| 曲阳县| 年辖:市辖区| 玉树县| 永善县| 黄骅市| 林州市| 那曲县| 东乌珠穆沁旗| 汾西县| 雷州市| 柘城县| 汨罗市| 黑水县| 区。| 永宁县|