jvm 方法内联_方法内联在JVM中有多积极?

jvm 方法内联

在IntelliJ IDEA中使用Ctrl + Alt + M 提取方法 。 Ctrl + Alt + M。 这就像选择一段代码并按此组合一样简单。 Eclipse也有它 。 我讨厌冗长的方法。 对于我来说,闻起来太久了:

public void processOnEndOfDay(Contract c) {if (DateUtils.addDays(c.getCreated(), 7).before(new Date())) {priorityHandling(c, OUTDATED_FEE);notifyOutdated(c);log.info("Outdated: {}", c);} else {if(sendNotifications) {notifyPending(c);}log.debug("Pending {}", c);}
}

首先,它具有不可读的条件。 无关紧要的实现方式,重要的是做什么。 因此,让我们首先提取它:

public void processOnEndOfDay(Contract c) {if (isOutdated(c)) {priorityHandling(c, OUTDATED_FEE);notifyOutdated(c);log.info("Outdated: {}", c);} else {if(sendNotifications) {notifyPending(c);}log.debug("Pending {}", c);}
}private boolean isOutdated(Contract c) {return DateUtils.addDays(c.getCreated(), 7).before(new Date());
}

显然,此方法实际上并不属于这里( F6 –移动实例方法):

public void processOnEndOfDay(Contract c) {if (c.isOutdated()) {priorityHandling(c, OUTDATED_FEE);notifyOutdated(c);log.info("Outdated: {}", c);} else {if(sendNotifications) {notifyPending(c);}log.debug("Pending {}", c);}
}

注意到不同了吗? 我的IDE使isOutdated()Contract的实例方法,听起来isOutdated() 。 但是我还是不开心。 这种方法发生了太多的事情。 一个分支执行一些与业务相关的priorityHandling() ,一些系统通知和日志记录。 其他分支执行条件通知和日志记录。 首先,让我们将处理过时的合同转移到单独的方法中:

public void processOnEndOfDay(Contract c) {if (c.isOutdated()) {handleOutdated(c);} else {if(sendNotifications) {notifyPending(c);}log.debug("Pending {}", c);}
}private void handleOutdated(Contract c) {priorityHandling(c, OUTDATED_FEE);notifyOutdated(c);log.info("Outdated: {}", c);
}

也许有人说这足够了,但是我看到分支之间惊人的不对称性。 handleOutdated()是非常高级的,而发送else分支是技术性的。 软件应易于阅读,因此请勿将不同级别的抽象彼此混用。 现在我很高兴:

public void processOnEndOfDay(Contract c) {if (c.isOutdated()) {handleOutdated(c);} else {stillPending(c);}
}private void handleOutdated(Contract c) {priorityHandling(c, OUTDATED_FEE);notifyOutdated(c);log.info("Outdated: {}", c);
}private void stillPending(Contract c) {if(sendNotifications) {notifyPending(c);}log.debug("Pending {}", c);
}

这个例子有些人为,但实际上我想证明一些不同的东西。 这些天并不经常出现,但是仍然有开发人员担心提取方法会认为它在运行时速度较慢。 他们无法理解JVM是一款很棒的软件(它可能远远超过Java语言),它内置了许多真正令人惊叹的运行时优化。 首先,较短的方法更容易推理。 流动更明显,范围更短,副作用更明显。 使用长方法,JVM可能会简单地放弃。 第二个原因更为重要:

方法内联

如果JVM发现一遍又一遍执行的小方法,它将简单地用该方法的主体替换该方法的每次调用。 以此为例:

private int add4(int x1, int x2, int x3, int x4) {return add2(x1, x2) + add2(x3, x4);
}private int add2(int x1, int x2) {return x1 + x2;
}

您可能几乎可以确定,一段时间后JVM将摆脱add2()并将代码转换为:

private int add4(int x1, int x2, int x3, int x4) {return x1 + x2 + x3 + x4;
}

重要说明是它是JVM,而不是编译器。 生成字节码时, javac非常保守,并将所有工作都留给了JVM。 这个设计决定非常出色:

  • JVM对目标环境,CPU,内存,体系结构了解更多,并且可以更积极地进行优化
  • JVM可以发现代码的运行时特征,例如,哪些方法最常执行,哪些虚拟方法只有一种实现等。
  • 使用旧Java编译的.class将在较新的JVM上运行得更快。 您很可能会更新Java,然后重新编译源代码。

让我们对所有这些假设进行测试。 我写了一个小程序,标题为“ 有史以来最糟糕的分而治之原则add128()接受128个参数(!),并调用add64()两次–参数的前一半和后一半。 add64()类似于,只是它两次调用add32() 。 我想您会明白的,最后我们要使用add2()来完成繁重的工作。 一些数字被截断以免引起您的注意 :

public class ConcreteAdder {public int add128(int x1, int x2, int x3, int x4, ... more ..., int x127, int x128) {return add64(x1, x2, x3, x4, ... more ..., x63, x64) +add64(x65, x66, x67, x68, ... more ..., x127, x128);}private int add64(int x1, int x2, int x3, int x4, ... more ..., int x63, int x64) {return add32(x1, x2, x3, x4, ... more ..., x31, x32) +add32(x33, x34, x35, x36, ... more ..., x63, x64);}private int add32(int x1, int x2, int x3, int x4, ... more ..., int x31, int x32) {return add16(x1, x2, x3, x4, ... more ..., x15, x16) +add16(x17, x18, x19, x20, ... more ..., x31, x32);}private int add16(int x1, int x2, int x3, int x4, ... more ..., int x15, int x16) {return add8(x1, x2, x3, x4, x5, x6, x7, x8) + add8(x9, x10, x11, x12, x13, x14, x15, x16);}private int add8(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8) {return add4(x1, x2, x3, x4) + add4(x5, x6, x7, x8);}private int add4(int x1, int x2, int x3, int x4) {return add2(x1, x2) + add2(x3, x4);}private int add2(int x1, int x2) {return x1 + x2;}}

不难看出,通过调用add128()我们总共进行了127个方法调用。 很多。 仅供参考,这里是一个简单的实现 :

public class InlineAdder {public int add128n(int x1, int x2, int x3, int x4, ... more ..., int x127, int x128) {return x1 + x2 + x3 + x4 + ... more ... + x127 + x128;}

最后,我还提供了一个使用abstract方法和继承的实现。 127个虚拟呼叫非常昂贵。 这些方法需要动态调度 ,因此要求更高,因为它们无法内联。 不能吗

public abstract class Adder {public abstract int add128(int x1, int x2, int x3, int x4, ... more ..., int x127, int x128);public abstract int add64(int x1, int x2, int x3, int x4, ... more ..., int x63, int x64);public abstract int add32(int x1, int x2, int x3, int x4, ... more ..., int x31, int x32);public abstract int add16(int x1, int x2, int x3, int x4, ... more ..., int x15, int x16);public abstract int add8(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8);public abstract int add4(int x1, int x2, int x3, int x4);public abstract int add2(int x1, int x2);
}

和一个实现:

public class VirtualAdder extends Adder {@Overridepublic int add128(int x1, int x2, int x3, int x4, ... more ..., int x128) {return add64(x1, x2, x3, x4, ... more ..., x63, x64) +add64(x65, x66, x67, x68, ... more ..., x127, x128);}@Overridepublic int add64(int x1, int x2, int x3, int x4, ... more ..., int x63, int x64) {return add32(x1, x2, x3, x4, ... more ..., x31, x32) +add32(x33, x34, x35, x36, ... more ..., x63, x64);}@Overridepublic int add32(int x1, int x2, int x3, int x4, ... more ..., int x32) {return add16(x1, x2, x3, x4, ... more ..., x15, x16) +add16(x17, x18, x19, x20, ... more ..., x31, x32);}@Overridepublic int add16(int x1, int x2, int x3, int x4, ... more ..., int x16) {return add8(x1, x2, x3, x4, x5, x6, x7, x8) + add8(x9, x10, x11, x12, x13, x14, x15, x16);}@Overridepublic int add8(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8) {return add4(x1, x2, x3, x4) + add4(x5, x6, x7, x8);}@Overridepublic int add4(int x1, int x2, int x3, int x4) {return add2(x1, x2) + add2(x3, x4);}@Overridepublic int add2(int x1, int x2) {return x1 + x2;}}

在有关@Cacheable开销的文章发表后,受到一些有趣的读者意见的鼓励,我编写了一个快速基准测试,以比较过度提取的ConcreteAdderVirtualAdder开销(以查看虚拟调用开销)。 结果出乎意料,有些模棱两可。 我在两台机器(蓝色和红色)和相同的软件上运行相同的基准测试,但是第二台具有更多内核,并且是64位:
图表

详细环境:

环境环境

事实证明,在一台速度较慢的计算机上 JVM决定内联所有内容。 不仅简单的private通话,而且虚拟的一次通话。 那怎么可能 很好,JVM发现Adder只有一个子类,因此每个abstract方法只有一个可能的版本。 如果在运行时加载另一个子类(或什至更多子类),则可能会看到性能下降,因为不再可能进行内联。 但是抛开细节,在此基准测试方法中调用并不便宜,实际上是免费的 ! 方法调用(具有极大的文档价值,提高了可读性)仅存在于源代码和字节码中。 在运行时,它们被完全消除(内联)。

我不太了解第二个基准。 看起来机器B的运行速度确实更快,但实际上运行参考SingleMethodCall基准测试的速度更快,但是其他机器甚至比A都慢。 也许它决定推迟内联? 差异是显着的,但并不是那么大。 同样,就像优化堆栈跟踪生成一样,如果您开始通过手动内联方法来优化代码,从而使它们变得更长和复杂,那么您正在解决错误的问题。

该基准可在GitHub上获得 ,以及文章来源 。 我鼓励您在设置中运行它。 此外,每个拉取请求都是自动在Travis上构建的,因此您可以在同一环境下轻松比较结果。

参考: JVM中方法内联的攻击性如何? 来自我们的JCG合作伙伴 Tomasz Nurkiewicz,来自Java和邻里博客。

翻译自: https://www.javacodegeeks.com/2013/02/how-aggressive-is-method-inlining-in-jvm.html

jvm 方法内联

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php ssh 连接mysql_通过PHP中的SSH连接到MySQL服务器?mysql-问答-阿里云开发者社区-阿里云...

SSH隧道解决方案 设置到MySQL数据库服务器的SSH隧道(最好是通过Jumpbox以获得安全性)。(A)GUI工具 根据您的要求,您可以使用具有内置SSH隧道支持的GUI MySQL客户端(例如SequelPro),使用Visual Studio代码 转发端口/创建SSH隧道或使用PuTTY设置端口转发。…

mybatis delete返回值_面试:谈谈你对MyBatis执行过程之SQL执行过程理解

前言在了解了MyBatis初始化加载过程后,我们也应该研究看看SQL执行过程是怎样执行?这样我们对于Mybatis的整个执行流程都熟悉了,在开发遇到问题也可以很快定位到问题。更重要的,在面试中遇到面试官咨询Mybatis的知识点的时候&#…

GC解释:堆

世代垃圾收集器 JVM堆分为两个不同的世代。 一种称为“年轻”,另一种称为“老”(有时称为终身制)。 年轻一代又分为两个主要的逻辑部分:伊甸园和幸存者空间。 垃圾收集人员还使用了虚拟空间供年轻人和老年人使用,以调整…

ubuntu设置mysql可以非本地访问_ubuntu server下设置mysql的远程访问权限

安装mysql安装mysql的方式较多主要有使用源安装,使用本地压缩包进行安装的方式。 具体参照 【ubuntu安装mysql5.5】检查mysql的远程访问权限eniZ944qtakg9Z:~$netstat -tl | grep mysql#tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTE…

python调用mysql数据库sql语句过长有问题吗_python操作mysql数据库,一次性执行多条语句不执行...

总结:1、使用这种方法,获取到的返回值总是最后一条命令的返回值,如果最后一条命令是update或者delete这种sql,则返回为() 2、sql的每一条语句后面都需要添加一个分号,不然最后splice时候 &#…

mysql sql wait 写法_有关SQL语句写法注意的那些事情(原创整理)

前段时候针对开发做的SQL语句写法方面注意点的培训,特意总结了一下,也共享一下。书写SQL需要注意的若干问题(MySQL版)一、基本问题1,在系统中运行的SQL查询,先考虑一下能不能在Slave上检索,目前各个项目中Master上的不…

mysql 时间函数多了8小时_MySQL时间函数了解

NOW() -- 获取当前格式化时间原表mysql> CREATE TABLE blog (-> id INT PRIMARY KEY auto_increment,-> NAME CHAR (32),-> sub_time datetime-> ); Query OK, 0 rows affected (0.01 sec)mysql> INSERT INTO blog (NAME, sub_time)-> VALUES…

JUnit 5 –基础

JUnit 5是适用于Java的下一代单元测试框架,具有许多有趣的功能,包括嵌套测试,参数化测试,新的扩展API或Java 8支持。 本文展示了JUnit 5的基本概念,包括测试生命周期,参数注入和断言(基本&…

mysql explain output_MySQL查询优化之explain的深入解析【转载】

在分析查询性能时,考虑EXPLAIN关键字同样很管用。EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作、以及MySQL成功返回结果集需要执行的行数。explain 可以帮助我们分析 select 语句,让我们知道查询效率低下的原因,从而改进我…

char强制类型转换为int_C语言学习第5篇---类型转换概念理解

类型之间的转换1.C语言中的数据可以进行转换---强制类型转换---隐式类型转换强制类型转换1.强制类型转换的语法---(Type)var_name;---(Type)value;2.强制类型转换的结果---目标类型能够容纳目标值,结果不变---目标类型…

python 中求最大值问题_Python中用max()方法求最大值的介绍

max() 方法返回其参数最大值:最接近正无穷大的值。语法以下是max()方法的语法: max( x, y, z, .... )参数 x -- 这是一个数值表达式。 y -- 这也是一个数值表达式。 z -- 这是一个数值表达式。返回值此方法返回其参数的最大值。例子下面的例子显示了max(…

在项目中使用fastreport_如何在项目管理中使用里程碑

不管您的项目是什么,完成大工作都意味着朝目标端点迈出几步。正如您无法结束旅程一样,您的团队必须完成第一步,以便从下一个步骤开始并最终完成项目。这可能是设定里程碑的最大原因。毕竟,如果没有完成日期,则项目的一…

java nio的演进_Java接口的防御性API演进

java nio的演进API的发展绝对是不平凡的。 只有少数几个需要处理的事情。 我们大多数人每天都在使用内部专有API。 现代IDE附带了很棒的工具,可以分解,重命名,上拉,下推,间接,委托,推断&#xf…

python层次聚类法画图_Python实现简单层次聚类算法以及可视化

本文实例为大家分享了Python实现简单层次聚类算法,以及可视化,供大家参考,具体内容如下基本的算法思路就是:把当前组间距离最小的两组合并成一组。算法的差异在算法如何确定组件的距离,一般有最大距离,最小…

range函数python2和3区别_【后端开发】range函数python2和3区别

range函数是一个用来创建算数级数序列的通用函数,返回一个[start, start step, start 2 * step, ...]结构的整数序列;py2中的range()函数用法:(推荐学习:Python视频教程) range()返回的是一个列表 >&g…

mysql AUTO_INC 是否锁_AUTO-INC锁的一些思考

AUTO-INC锁是一个表级锁,这种锁是作用于语句的而不是事务(即语句执行完了锁就会被释放)。innodb_autoinc_lock_mode用于控制AUTO-INC的锁表逻辑,可能的取值为0,1,2.innodb_autoinc_lock_mode0 (每次都会产生表锁)不管是对于simple inserts(insert into v…

Java 8可选

在编程时,我们都面临着最( 臭名昭著 )的NullPointerException 。 而且我相信我们所有人都同意,遇到NullPointerException也是一种痛苦。 为了使读者了解最新情况,著名的计算机科学家Tony Hoare引入了空引用&#xff0c…

matlab cat函数_如何用Matlab编写贪吃蛇游戏?(持续更新)

今后我们实验室的研究重点将会聚焦在“基于游戏的测评”和”教育游戏化“这两个主题上,因此很有必要研究实现“爆款”游戏的一些基本的技术方法。这篇文章将介绍如何借助Matlab GUI 面向对象编程技术实现贪吃蛇游戏。所有的游戏都可以解构成至少两个层次&#xff1…

python程序设计案例课堂第二篇_Python程序设计案例课堂第二篇核心技术第十章图形用户界面...

第10章 图形用户界面保存并运行程序,结果如图10-1所示。C:\Users\Administrator>python d:\python\ch10\10.1.py图10-1 程序运行结果【案例剖析】上述代码的含义分析如下。(1) 第1行:加载tkinter 模块。(2) 第 2 行:使用tkinter 模块的Tk(…

fprintf函数的用法matlab_极力推荐这个Matlab教程

点击蓝字 关注我们儿童节快乐前些日子, 由于一些原因的需要, 又把曾经的毕业论文拿来, 改吧改吧, 发现有些地方真的惨目忍睹, 只怪当时太年轻, 没想到给自己挖了一个大坑, 不,, 应该称之为巨坑, 对于论文中涉及到的代码, 当时主要使用了伪代码的形式, 以及现学现用的Python, 可…