Lambda首次使用很慢怎么办?看法宝

文章探讨了Lambda表达式首次使用时速度慢的原因,通过猜想与验证,排除了操作系统预热和JIT即时编译的影响。研究发现,问题在于Lambda首次使用时需要加载LambdaMetafactory类和ASM框架,导致编译和加载时间增加。了解Lambda的实现原理,得知其并非匿名内部类的语法糖,而是优化后的实现。结论指出,Lambda的效率并不低,只是需要一次性的预热过程。
摘要由CSDN通过智能技术生成

描述的话不多说,直接上图:

Lambda初次使用很慢?从JIT到类加载再到实现原理

看到输出结果了吗?为什么第一次和第二次的时间相差如此之多?咱们一起琢磨琢磨,也可以先去看看结论再回过头看分析

注:并非仅第二次快,而是除了第一次,之后的每一次都很快

给与猜想

  1. 是否和操作系统预热有关?
  2. 是否和JIT(即时编译)有关?
  3. 是否和ClassLoader类加载有关?
  4. 是否和Lambda有关,并非foreach的问题

验证猜想

操作系统预热

操作系统预热这个概念是我咨询一位大佬得到的结论,在百度 / Google 中并未搜索到相应的词汇,但是在模拟测试中,我用 普通遍历 的方式进行测试:

Lambda初次使用很慢?从JIT到类加载再到实现原理

基本上每次都是前几次速度较慢,后面的速度更快,因此 可能 有这个因素影响,但差距并不会很大,因此该结论并不能作为问题的答案。

JIT 即时编译

首先介绍一下什么是JIT即时编译:

当 JVM 的初始化完成后,类在调用执行过程中,执行引擎会把字节码转为机器码,然后在操作系统中才能执行。在字节码转换为机器码的过程中,虚拟机中还存在着一道编译,那就是即时编译。

最初,JVM 中的字节码是由解释器( Interpreter )完成编译的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为热点代码。

为了提高热点代码的执行效率,在运行时,即时编译器(JIT,Just In Time)会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,然后保存到内存中

再来一个概念,回边计数器

回边计数器用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为 "回边"(Back Edge)

建立回边计数器的主要目的是为了触发 OSR(On StackReplacement)编译,即栈上编译,在一些循环周期比较长的代码段中,当循环达到回边计数器阈值时,JVM 会认为这段是热点代码,JIT 编译器就会将这段代码编译成机器语言并缓存,在该循环时间段内,会直接将执行代码替换,执行缓存的机器语言

从上述的概念中,我们应该可以得到一个结论:第一条所谓的操作系统预热 大概率不正确,因为普通遍历方法执行N次,后续执行的时间占用比较小,很可能是因为JIT导致的。

那 JIT即时编译 是否是最终的答案?我们想办法把 JIT 关掉来测试一下,通过查询资料发现了如下内容:

Procedure

  • Use the -D option on the JVM command line to set the java.compiler property to NONE or the empty string. Type the following command at a shell or command prompt: java -Djava.compiler=NONE <class> 复制代码

注:该段内容来自IBM官方资料,地址见 <收获> ,咱们先不要停止思考

通过配置 IDEA JVM 参数:

Lambda初次使用很慢?从JIT到类加载再到实现原理

执行问题中的代码测试结果如下:

# 禁用前
foreach time one: 38
分割线...
foreach time two: 1

# 禁用后
forea
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值