使用JMH做Benchmark基准测试

匿名 (未验证) 提交于 2019-12-02 21:45:52

前言

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!