Java的几种创建实例方法的性能对比
备注于 2019-08-10:
以上是我对Lambda原理比较模糊时的测试,现在觉得唯一的用处在于对比第二篇得出在循环中使用Lambda会慢很多。
实际运用的话,我建议看下一篇:Java的几种创建实例方法的性能对比(二)
近来打算自己封装一个比较方便读写的Office Excel 工具类,前面已经写了一些,比较粗糙本就计划重构一下,刚好公司的电商APP后台原有的导出Excel实现出现了可怕的性能问题,600行的数据生成Excel工作簿居然需要50秒以上,客户端连接都被熔断了还没导出来,挺巧,那就一起解决吧。
在上一个版本里呢,我认为比较巧妙的地方在于用函数式编程的方式代替反射,很早以前了解了反射的一些底层后我就知道反射的性能很差,但一直没实际测试过各种调用场景的性能差距。
至于底层字节码、CPU指令这些我就不深究了,我还没到那个级别,那这次就来个简单的测试吧。
目标:创建Man对象。
方式:
① 直接引用 new Man();
② 使用反射
③ 使用内部类
④ 使用Lombda表达式
⑤ 使用Method Reference
在学习Java8新特性的时候,我所了解到的是Lombda表达式是内部类的一种简化书写方式,也就是语法糖,但两者间在运行时居然有比较明显的性能差距,让我不得不怀疑它底层到底是啥东西,时间精力有限先记着,有必要的时候再去啃openJDK吧。
还有就是Lombda和Method Reference从表现来看,底层应该是同一个东西,但IDEA既然分开两种内部类的写法推荐,那就分开对待吧。
测试时每种方式循环调用 1 亿次,每种方式连续计算两次时间,然后对比第二次运行的结果,直接run没有采用debug运行。
贴代码:
package com.supalle.test; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Supplier; /**
* @描述:语法PK
* @作者:Supalle
* @时间:2019/7/26
*/
public class SyntaxPKTest { /* 循环次数 */
private final static int SIZE = 100000000; /* 有类如下 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
private static class Man {
private String name;
private int age;
} /**
* 使用 new Man();
*
* @return 运行耗时
*/
public static long runWithNewConstructor() {
long start = System.currentTimeMillis(); for (int i = 0; i < SIZE; i++) {
new SyntaxPKTest.Man();
} return System.currentTimeMillis() - start;
} /**
* 使用反射
*
* @return 运行耗时
*/
public static long runWithReflex() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<SyntaxPKTest.Man> constructor = SyntaxPKTest.Man.class.getConstructor();
long start = System.currentTimeMillis(); for (int i = 0; i < SIZE; i++) {
constructor.newInstance();
} return System.currentTimeMillis() - start;
} /**
* 使用内部类调用 new Man();
*
* @return 运行耗时
*/
public static long runWithSubClass() {
long start = System.currentTimeMillis(); for (int i = 0; i < SIZE; i++) {
new Supplier<SyntaxPKTest.Man>() {
@Override
public SyntaxPKTest.Man get() {
return new SyntaxPKTest.Man();
}
}.get(); } return System.currentTimeMillis() - start;
} /**
* 使用Lambda调用 new Man();
*
* @return 运行耗时
*/
public static long runWithLambda() {
long start = System.currentTimeMillis(); for (int i = 0; i < SIZE; i++) {
((Supplier<SyntaxPKTest.Man>) () -> new SyntaxPKTest.Man()).get();
} return System.currentTimeMillis() - start;
} /**
* 使用 MethodReference
*
* @return 运行耗时
*/
public static long runWithMethodReference() {
long start = System.currentTimeMillis(); for (int i = 0; i < SIZE; i++) {
((Supplier<SyntaxPKTest.Man>) SyntaxPKTest.Man::new).get();
} return System.currentTimeMillis() - start;
} public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 测试前调用一下,加载Man字节码,尽量公平
SyntaxPKTest.Man man1 = new SyntaxPKTest.Man();
SyntaxPKTest.Man man2 = new SyntaxPKTest.Man("张三", 20); System.out.println("测试环境:CPU核心数 - " + Runtime.getRuntime().availableProcessors()); System.out.println(); // 这里的话对比再次调用的时间
System.out.println("首次使用 new Man() 耗时:" + runWithNewConstructor());
System.err.println("再次使用 new Man() 耗时:" + runWithNewConstructor());
System.out.println("首次使用反射 耗时:" + runWithReflex());
System.err.println("再次使用反射 耗时:" + runWithReflex());
System.out.println("首次使用内部类调用 new Man() 耗时:" + runWithSubClass());
System.err.println("再次使用内部类调用 new Man() 耗时:" + runWithSubClass());
System.out.println("首次使用Lambda调用 new Man() 耗时:" + runWithLambda());
System.err.println("再次使用Lambda调用 new Man() 耗时:" + runWithLambda());
System.out.println("首次使用 MethodReference 耗时:" + runWithMethodReference());
System.err.println("再次使用 MethodReference 耗时:" + runWithMethodReference()); } }
运行结果:
一:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:5
再次使用 new Man() 耗时:3
首次使用反射 耗时:312
再次使用反射 耗时:276
首次使用内部类调用 new Man() 耗时:6
再次使用内部类调用 new Man() 耗时:3
首次使用Lambda调用 new Man() 耗时:142
再次使用Lambda调用 new Man() 耗时:100
首次使用 MethodReference 耗时:86
再次使用 MethodReference 耗时:85
二:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:5
再次使用 new Man() 耗时:2
首次使用反射 耗时:326
再次使用反射 耗时:275
首次使用内部类调用 new Man() 耗时:6
再次使用内部类调用 new Man() 耗时:3
首次使用Lambda调用 new Man() 耗时:122
再次使用Lambda调用 new Man() 耗时:86
首次使用 MethodReference 耗时:102
再次使用 MethodReference 耗时:83
三:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:5
再次使用 new Man() 耗时:3
首次使用反射 耗时:322
再次使用反射 耗时:288
首次使用内部类调用 new Man() 耗时:7
再次使用内部类调用 new Man() 耗时:2
首次使用Lambda调用 new Man() 耗时:128
再次使用Lambda调用 new Man() 耗时:92
首次使用 MethodReference 耗时:97
再次使用 MethodReference 耗时:81
如果Lambda和MethodReference调换一下位置如下:
1 System.out.println("首次使用 new Man() 耗时:" + runWithNewConstructor());
System.err.println("再次使用 new Man() 耗时:" + runWithNewConstructor());
System.out.println("首次使用反射 耗时:" + runWithReflex());
System.err.println("再次使用反射 耗时:" + runWithReflex());
System.out.println("首次使用内部类调用 new Man() 耗时:" + runWithSubClass());
System.err.println("再次使用内部类调用 new Man() 耗时:" + runWithSubClass());
7 System.out.println("首次使用 MethodReference 耗时:" + runWithMethodReference());
8 System.err.println("再次使用 MethodReference 耗时:" + runWithMethodReference());
System.out.println("首次使用Lambda调用 new Man() 耗时:" + runWithLambda());
System.err.println("再次使用Lambda调用 new Man() 耗时:" + runWithLambda());
一:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:6
再次使用 new Man() 耗时:2
首次使用反射 耗时:351
再次使用反射 耗时:270
首次使用内部类调用 new Man() 耗时:6
再次使用内部类调用 new Man() 耗时:3
首次使用 MethodReference 耗时:128
再次使用 MethodReference 耗时:97
首次使用Lambda调用 new Man() 耗时:82
再次使用Lambda调用 new Man() 耗时:74
二:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:5
再次使用 new Man() 耗时:3
首次使用反射 耗时:318
再次使用反射 耗时:297
首次使用内部类调用 new Man() 耗时:6
再次使用内部类调用 new Man() 耗时:2
首次使用 MethodReference 耗时:117
再次使用 MethodReference 耗时:100
首次使用Lambda调用 new Man() 耗时:91
再次使用Lambda调用 new Man() 耗时:79
三:
测试环境:CPU核心数 - 8 首次使用 new Man() 耗时:6
再次使用 new Man() 耗时:3
首次使用反射 耗时:319
再次使用反射 耗时:277
首次使用内部类调用 new Man() 耗时:8
再次使用内部类调用 new Man() 耗时:3
首次使用 MethodReference 耗时:139
再次使用 MethodReference 耗时:85
首次使用Lambda调用 new Man() 耗时:103
再次使用Lambda调用 new Man() 耗时:84
总结:
① 如果不需要足够的灵活性,直接使用 new 来构造一个对象,效率最高的。
② 反射确确实实是垫底,当然它也给我们提供了足够全面的、灵活的类操纵能力。
③ 使用内部类的方式,效率上和直接new 非常贴近,虽然看起来代码多一些,但是足够灵活。
④ Lambda和Method Reference效率其实很贴近,又是一起在Java8推出的,底层实现应该是一样的,在效率上比起反射好很多。
上个版本中,我使用的Method Reference,下个版本还会继续使用Method Reference,因为接口方式和内部类一致,如果碰到某些对性能要求非常极致的使用场景,可以在使用时以内部类的方式替代Method Reference而不需要改变工具类的代码。
备注于 2019-08-10:
以上是我对Lambda原理比较模糊时的测试,现在觉得唯一的用处在于对比第二篇得出在循环中使用Lambda会慢很多。
实际运用的话,我建议看下一篇:Java的几种创建实例方法的性能对比(二)
Java的几种创建实例方法的性能对比的更多相关文章
- Java的几种创建实例方法的性能对比(二)
上一篇里对几种书写方式进行了简单的测试,得出了一些初步的结论.这次简单了解Lambda原理后,对测试做了一些调整,发现得到不一样的结果,而这个调整,明显更契合实际开发的场景. 暂时还没有亲自去验证,主 ...
- Java中两种实现多线程方式的对比分析
本文转载自:http://www.linuxidc.com/Linux/2013-12/93690.htm#0-tsina-1-14812-397232819ff9a47a7b7e80a40613cf ...
- Go_18: Golang 中三种读取文件发放性能对比
Golang 中读取文件大概有三种方法,分别为: 1. 通过原生态 io 包中的 read 方法进行读取 2. 通过 io/ioutil 包提供的 read 方法进行读取 3. 通过 bufio 包提 ...
- Golang 中三种读取文件发放性能对比
Golang 中读取文件大概有三种方法,分别为: 1. 通过原生态 io 包中的 read 方法进行读取 2. 通过 io/ioutil 包提供的 read 方法进行读取 3. 通过 bufio 包提 ...
- 求斐波那契数列第n位的几种实现方式及性能对比(c#语言)
在每一种编程语言里,斐波那契数列的计算方式都是一个经典的话题.它可能有很多种计算方式,例如:递归.迭代.数学公式.哪种算法最容易理解,哪种算法是性能最好的呢? 这里给大家分享一下我对它的研究和总结:下 ...
- java线程——三种创建线程的方式
前言 线程,英文Thread.在java中,创建线程的方式有三种: 1.Thread 2.Runnable 3.Callable 在详细介绍下这几种方式之前,我们先来看下Thread类和Runnabl ...
- Java数组3种创建方式
public static void main(String[] args){ /** * 1. 固定大小的空数组, 动态创建 */ String[] strArr1 = new String[3]; ...
- Delegate、Thread、Task、ThreadPool几种方式创建异步任务性能对比
开始预测的结果是 Task>Delegate>ThreadPool>>Thread. (一)测试代码 static async Task<int> AsyncTas ...
- 几种流行Webservice框架性能对比
1 摘要 开发webservice应用程序中离不开框架的支持,当open-open网站列举的就有30多种,这对于开发者如何选择带来一定的疑惑.性能Webservice的关键要素,不同的框架性 ...
随机推荐
- Kafka基本概念介绍
Kafka官方介绍:Kafka是一个分布式的流处理平台(0.10.x版本),在kafka0.8.x版本的时候,kafka主要是作为一个分布式的.可分区的.具有副本数的日志服务系统(Kafka™ is ...
- 如何自学PHP做一个网站 PHP可以做什么项目?网站 小程序 公众号能用PHP开发吗?
很多想从事程序开发的人员,想自学一门语言,不知道从哪里下手学习,如何入门学习?今天我们就以PHP为例子,来讲述一下如何快速的学习一门开发语言,让你快速入门.PHP是一个什么语言?它能开发什么项目呢?下 ...
- 前端自动化工具gulp入门基础
gulp是前端开发过程中经常要用到的工具,非常值得花时间去掌握.利用gulp,我们可以使产品流程脚本化,节约大量的时间,有条不紊地进行业务开发.本文简单讲一下入门gulp需要掌握的东西. 安装gulp ...
- F#周报2019年第26期
新闻 逐渐演化的.NET Core框架 Visual Studio提示与技巧 Windows Termina(预览) Microsoft在GitHub上的工程师从2000名增加至25000名 视频及幻 ...
- Storm —— 集群环境搭建
一.集群规划 这里搭建一个3节点的Storm集群:三台主机上均部署Supervisor和LogViewer服务.同时为了保证高可用,除了在hadoop001上部署主Nimbus服务外,还在hadoop ...
- 【springBoot】SpringBoot修改启动logo图案
修改boot启动banner logo看到比较好玩,就存一下~ (1)我们在src/main/resources下新建一个banner.txt文件. (2)通过http://patorjk.com/s ...
- Appium+python自动化(十三)- 输入中文 - 一次填坑记(超详解)
简介 无论你在哪里,在做什么都会遇到很多坑,这些坑有些事别人挖的,有些是自己挖的.别人挖的叫坑人,自己挖的叫自杀,儿子挖的叫坑爹.因此在做app自动化道路上也不会是一帆风顺的,你会踩很多坑,这些坑和你 ...
- NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s!
今天测试环境一虚拟机运行中突然报错,,, 没见过的内核报错,于是google一番. 系统日志: Nov :: dev- kernel: NMI watchdog: BUG: soft lockup - ...
- 微服务SpringCloud之服务调用与负载均衡
上一篇我们学习了服务的注册与发现,本篇博客是在上一篇的基础上学习服务的调用.上一博客主要创建了Eureka的服务端和一个Client,该Client包含了一个Controller用来提供对外服务供外部 ...
- 03_javaSE面试题:类初始化和实例初始化
题目 下面代码运行的结果是什么? Father 类 /** * @author kevin * @date 2019/7/8 15:48 */ public class Father { privat ...