前言

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方式的方法调用执行性能差不多,实际可根据代理对象有无接口进行选择。

Jmh测试JDK,CGLIB,JAVASSIST动态代理方式的性能的更多相关文章

  1. java两种动态代理方式的理解

    要理解动态代理,不妨先来看看一个静态代理的例子. 一.静态代理 以一个电商项目的例子来说明问题,比如我定义了一个订单的接口IOrder,其中有一个方法时delivery,代码如下. package c ...

  2. JDK 和 CGLib 实现动态代理和区别

    JDK 和 CGLib 实现动态代理和区别 在日常的开发中,Spring AOP 是一个非常常用的功能.谈到 AOP,自然离不开动态代理. 那么,基于 JDK 和 CGLib 如何实现动态代理,他们之 ...

  3. Spring AOP --JDK动态代理方式

    我们知道Spring是通过JDK或者CGLib实现动态代理的,今天我们讨论一下JDK实现动态代理的原理. 一.简述 Spring在解析Bean的定义之后会将Bean的定义生成一个BeanDefinit ...

  4. 基于jdk proxy的动态代理模式

    代理模式 是spring AOP机制的实现基础,有必要学习一下. 有两种,一种是目标类有接口的, 采用JDK动态代理,一种是目标类没接口的,采用CGLIB动态代理. 先看一组代码, package c ...

  5. JAVAEE——Mybatis第一天:入门、jdbc存在的问题、架构介绍、入门程序、Dao的开发方法、接口的动态代理方式、SqlMapConfig.xml文件说明

    1. 学习计划 第一天: 1.Mybatis的介绍 2.Mybatis的入门 a) 使用jdbc操作数据库存在的问题 b) Mybatis的架构 c) Mybatis的入门程序 3.Dao的开发方法 ...

  6. JDK设计模式之—动态代理

    代理模式的特点 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口.代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类. 代理类的对象并不是真正实现服务,而是通过调用委 ...

  7. cglib的动态代理

    前言 jdk中的动态代理通过反射类Proxy和InvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性. cglib ...

  8. Mybatis进阶学习笔记——动态代理方式开发Dao接口、Dao层(推荐第二种)

    1.原始方法开发Dao Dao接口 package cn.sm1234.dao; import java.util.List; import cn.sm1234.domain.Customer; pu ...

  9. cglib实现动态代理简单使用

    Boss: package proxy.cglib; public class Boss{ public void findPerson() { System.out.println("我要 ...

随机推荐

  1. Matlab随笔之判别分析

    原文:Matlab随笔之判别分析 从概率论角度,判别分析是根据所给样本数据,对所给的未分类数据进行分类. 如下表,已知有t个样本数据,每个数据关于n个量化特征有一个值,又已知该样本数据的分类,据此,求 ...

  2. 使用Photoshop切图的三种方式

    PhotoShop切图的三种方式 1. 原始切图 (1)选择工具栏中的切片工具 (2)找到要切片的元素,在右侧的图层框中,使元素背景隐藏,然后用切片工具选择需要切片的元素     (3)导出为web常 ...

  3. 协程在Web服务器中的应用(配的图还不错)

    协程(纤程,微线程)这个概念早就有之,各家互联网公司也都有研究,但在国内各大论坛和大会热起来,还是今年的事. 最近参与讨论开放平台建设和架构设计过程中,有同事提到了使用协程代替线程,能够很大幅度的提高 ...

  4. XF 标签页面

    using System; using Xamarin.Forms; using Xamarin.Forms.Xaml; [assembly: XamlCompilation (XamlCompila ...

  5. Delphi跨平台Socket通讯库

    盒子中的souledge大侠发布了新的Socket库,以下为原文: 我之前写过一个iocp的框架,放到googlecode上了. 由于当时的delphi版本尚无法跨平台,所以该框架只能运行在Windo ...

  6. vs2015 cordova环境安装【个人遇到的几个问题】

    原文:vs2015 cordova环境安装[个人遇到的几个问题] 问题1: vs2015,设置  Debug  Android 设备[真机调试] Exception in thread "m ...

  7. Win8Metro(C#)数字图像处理--2.19图像水平镜像

    原文:Win8Metro(C#)数字图像处理--2.19图像水平镜像  [函数名称] 图像水平镜像函数MirrorXProcess(WriteableBitmap src) [函数代码]      ...

  8. C#程序以管理员的身份运行

    在一些特定的情况下我们需要能够有管理员的权限,这样我们的很多执行,或者写入就不会报错了. 1.解决方案资源管理器---->项目(右键)--->属性-->安全性 2.勾选“启用Clic ...

  9. ARTS 1.7 - 1.11

    每周一个 Algorithm,Review 一篇英文文章,总结一个工作中的技术 Tip,以及 Share 一个传递价值观的东西! Algorithm: 学习算法 题目: https://leetcod ...

  10. VS点击调试卡住的问题解决方案(转载)

    本来今天好好的,不知道弄到了什么,调试不了了,一点击立马卡住,就一直在那转,就在网上找了找解决方案,下面给大家列出来几种可能会卡住的问题已经解决方案 1:加载调试符号引起的卡住 解决方案: 在“选项” ...