Jmh測試JDK,CGLIB,JAVASSIST動態代理方式的性能

来源:https://www.cnblogs.com/lgjlife/archive/2019/07/11/11167617.html
-Advertisement-
Play Games

前言 JDK,CGLIB,JAVASSIST是常用的動態代理方式。 JDK動態代理僅能對具有介面的類進行代理。 CGLIB動態代理方式的目標類可以沒有介面。 Javassist是一個開源的分析、編輯和創建Java位元組碼的類庫,JAVASSIST可以動態修改類,比如添加方法和屬性。JAVASSIST的 ...


 

前言

JDK,CGLIB,JAVASSIST是常用的動態代理方式。

JDK動態代理僅能對具有介面的類進行代理。

CGLIB動態代理方式的目標類可以沒有介面。

Javassist是一個開源的分析、編輯和創建Java位元組碼的類庫,JAVASSIST可以動態修改類,比如添加方法和屬性。JAVASSIST的目標類也沒有介面限制。

動態代理常用在RPC介面調用中,因此選擇一個好的動態代理方式,會對系統性能有一定的提升。

 

對於代碼的性能測試,常規的方法如下,如此是無法獲取到準確的性能數據的

long start = System.currentTimeMillis();
xxx.xx();
long end = System.currentTimeMillis();
System.out.println("運行時間:"+(end-start));

 

JMH用來做基準測試,由於JIT編譯器會根據代碼運行情況進行優化,代碼在第一次執行的時候,速度相對較慢,隨著運行的次數增加,JIT編譯器會對代碼進行優化,以達到最佳的性能狀態。

JMH可以對代碼進行預熱,讓代碼達到最佳的性能狀態,再進行性能測試。

更詳細的說明參考: 使用JMH做Benchmark基準測試

 

本博客主要講解使用JMH對這三種動態代理的對象創建過程和方法調用進行測試。JDK版本是8.0.

代理實現

Jdk方式

public class JdkInvocationHandler  implements InvocationHandler {

    private Object target = null;
    public JdkInvocationHandler(Object object){
        this.target = object;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result = method.invoke(this.target,args);
        return result;
    }

    public  Object getProxy(){
        Object object =    Proxy.newProxyInstance(
                this.target.getClass().getClassLoader(),
                this.target.getClass().getInterfaces(),
                this);
        return  object;
    }

}

 

Cglib方式

引入pom

     <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.5</version>
        </dependency>

 

代碼

public class CglibProxy  implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public  Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();

    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        Object result = methodProxy.invokeSuper(o,objects);

        return result;
    }
}

 

Javassist方式

pom

    <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.20</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.20</version>
        </dependency>

 

代碼

public class JavassistProxy {

    public <T> T getProxy(Class<T> interfaceClass){
        ProxyFactory proxyFactory  = new ProxyFactory();

        if(interfaceClass.isInterface()){
            Class[] clz  = new Class[1];
            clz[0] = interfaceClass;
            proxyFactory.setInterfaces(clz);
        }
        else {
            proxyFactory.setSuperclass(interfaceClass);
        }
        proxyFactory.setHandler(new MethodHandler() {
            public Object invoke(Object proxy, Method method, Method method1, Object[] args) throws Throwable {
                Object result = method1.invoke(proxy,args);
                return  result;
            }
        });
        try{
            T bean =  (T)proxyFactory.createClass().newInstance();
            return  bean;
        }
        catch(Exception ex){
            log.error("Javassit 創建代理失敗:{}",ex.getMessage());
            return null;
        }
    }

}

 

性能測試

創建性能測試

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ProxyCreateTest {

    public static void main(String args[]) throws Exception{

        Options ops = new OptionsBuilder().include(ProxyCreateTest.class.getSimpleName())
                .forks(1).build();
        new Runner(ops).run();
    }

    @Benchmark
    public void CglibProxyCreate(){
        ProxyService proxyService =  (ProxyService)new CglibProxy().getProxy(ProxyServiceImpl.class);
    }


   @Benchmark
    public void JdkProxyCreate(){
       ProxyService proxyService = (ProxyService) new JdkInvocationHandler(new ProxyServiceImpl()).getProxy();
    }

    @Benchmark
    public void JavassistProxyCreate(){
        ProxyService proxyService = (ProxyService)  new JavassistProxy().getProxy(ProxyServiceImpl.class);
    }

}

 

 測試結果

  第一次測試
* Benchmark Mode Cnt Score Error Units * ProxyCreateTest.CglibProxyCreate avgt 20 192.691 ± 5.962 ns/op * ProxyCreateTest.JavassistProxyCreate avgt 20 2741254.026 ± 334384.484 ns/op * ProxyCreateTest.JdkProxyCreate avgt 20 130.982 ± 14.467 ns/op * * 第二次測試 * Benchmark Mode Cnt Score Error Units * ProxyCreateTest.CglibProxyCreate avgt 20 212.150 ± 15.399 ns/op * ProxyCreateTest.JavassistProxyCreate avgt 20 2995729.108 ± 265629.897 ns/op * ProxyCreateTest.JdkProxyCreate avgt 20 124.842 ± 8.404 ns/op *
第三次測試 * Benchmark Mode Cnt Score Error Units * ProxyCreateTest.CglibProxyCreate avgt 20 206.603 ± 6.834 ns/op * ProxyCreateTest.JavassistProxyCreate avgt 20 2979335.282 ± 290935.626 ns/op * ProxyCreateTest.JdkProxyCreate avgt 20 129.260 ± 9.020 ns/op

 

從測試結果來看,javassist的代理對象創建性能最差。最好的是JDK方式。

調用性能測試

//所有測試線程共用一個實例
@State(Scope.Benchmark)
//調用的平均時間,例如“每次調用平均耗時xxx毫秒”,單位是時間/操作數 @BenchmarkMode(Mode.AverageTime)
//單位為納秒 @OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ProxyRunTest { private ProxyService proxyServiceCglib = (ProxyService)new CglibProxy().getProxy(ProxyServiceImpl.class); private ProxyService proxyServiceJdk = (ProxyService) new JdkInvocationHandler(new ProxyServiceImpl()).getProxy(); private ProxyService proxyServiceJavassist = (ProxyService) new JavassistProxy().getProxy(ProxyServiceImpl.class); public static void main(String args[]) throws Exception{ Options ops = new OptionsBuilder().include(ProxyRunTest.class.getSimpleName()) .forks(1).build(); new Runner(ops).run(); } //方法註解,表示該方法是需要進行 benchmark 的對象。 @Benchmark public void CglibProxyRun(){ proxyServiceCglib.run(); } @Benchmark public void JdkProxyRun(){ proxyServiceJdk.run(); } @Benchmark public void JavassistProxyRun(){ proxyServiceJavassist.run(); } }

 

測試結果

 

第一次測試
Benchmark Mode Cnt Score Error Units ProxyRunTest.CglibProxyRun avgt
20 9.918 ± 1.268 ns/op ProxyRunTest.JavassistProxyRun avgt 20 34.226 ± 2.655 ns/op ProxyRunTest.JdkProxyRun avgt 20 5.225 ± 0.449 ns/op 第二次測試 Benchmark Mode Cnt Score Error Units ProxyRunTest.CglibProxyRun avgt 20 6.975 ± 0.629 ns/op ProxyRunTest.JavassistProxyRun avgt 20 31.707 ± 0.885 ns/op ProxyRunTest.JdkProxyRun avgt 20 5.442 ± 0.514 ns/op 第三次測試 Benchmark Mode Cnt Score Error Units ProxyRunTest.CglibProxyRun avgt 20 8.079 ± 1.381 ns/op ProxyRunTest.JavassistProxyRun avgt 20 33.916 ± 2.904 ns/op ProxyRunTest.JdkProxyRun avgt 20 5.947 ± 0.498 ns/op

 

從測試結果來看,javassist的代理對象調用執行性能最差。最好的是JDK方式。

總結

1.不管是代理創建還是方法調用執行,Javassist方式的性能最好,JDK的方式最差。

2.對於實際使用過程中。代理對象一般只會創建一次,創建完成後緩存起來供後續使用,因此創建過程的性能對整體性能影響不大。JDK方式和CGLIB方式的方法調用執行性能差不多,實際可根據代理對象有無介面進行選擇。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 通過前篇文章 "《設計模式:工廠模式,解除耦合的利器》" 的介紹,我們對工廠模式有了深入的瞭解,今天繼續介紹一種特殊的工廠模式,也就是抽象工廠模式。 定義 抽象工廠模式:提供一個創建一系列相關或相互依賴對象的介面,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬於對象創建型模式,是工廠方法模 ...
  • SpringCloud系列教程 | 第十一篇:使用Spring Cloud Sleuth和Zipkin進行分散式鏈路跟蹤 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如無特殊說明,本系列教程全採用以上版本 在分散式服務架構中,需要對分散式 ...
  • 微服務概述 1.1 易於擴展 1.2 部署簡單 1.3 技術異構性 資料庫的服務化切分 2.1 什麼是“分庫分表”? 2.2 資料庫擴展的幾種方式 2.3 分庫分表的幾種方式 2.4 引入分庫分表中間件後面臨的問題 2.5 現有分庫分表中間件的橫向對比 微服務架構中的分散式事務 3.1 什麼是事務? ...
  • 為什麼是kafka? 在我們大量使用分散式資料庫、分散式計算集群的時候,是否會遇到這樣的一些問題: 我們想分析下用戶行為(pageviews),以便我們設計出更好的廣告位 我想對用戶的搜索關鍵詞進行統計,分析出當前的流行趨勢 有些數據,存儲資料庫浪費,直接存儲硬碟效率又低 這些場景都有一個共同點: ...
  • 將自己的學習筆記般過來 https://owen027.github.io/2019/07/08/designPatterns/ JavaScript是一門 "[1]" "[2]" "[3]" ,設計模式通過對面向對象的特征 封裝、繼承、組合、多態 等技術的反覆使用,提煉出可復用的面向對象設計技巧。 ...
  • Introduction spring boot plus是一套集成spring boot常用開發組件的後臺快速開發框架 Purpose 每個人都可以獨立、快速、高效地開發項目! Everyone can develop projects independently, quickly and eff ...
  • 一、傳統Session認證 1、認證過程 2、存在問題 二、JWT簡介 JWT(全稱:JSON Web Token),在基於HTTP通信過程中,進行身份認證。 1、認證流程 2、JWT結構說明 抓一隻鮮活的Token過來。 上面的Token被手動格式化了,實際上是用"."分隔的一個完整的長字元串。 ...
  • 大家怎麼說? 老師很好,我認為,若想學好python,應該多練、多想、多看。學習資料不能僅限於老師給定的這些內容,這些畢竟是入門資料 老師講的真不錯,對於我們這種小白來說 也比較容易懂,雖然有些時候自己學起來可能比較費勁 ,但是已經很不錯了 哈哈哈 完全小白,在網上找了好多課程,不是講的太籠統就是不 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...