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,一经查实,立即删除!

相关文章

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…

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…

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

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

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

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

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

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

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, 可…

java中void_关于java中void的用法?

阿波罗的战车void除了说明该方法没有返回值外,还有什么作用呢?构造方法同样也是没有返回值的,那它和void方法有什么区别呢?构造方法与方法又有什么区别?用具象的实物来表现的话三者有何种关系呢?id 除了说明…

卷积神经网络mnist手写数字识别代码_搭建经典LeNet5 CNN卷积神经网络对Mnist手写数字数据识别实例与注释讲解,准确率达到97%...

LeNet-5卷积神经网络是最经典的卷积网络之一,这篇文章就在LeNet-5的基础上加入了一些tensorflow的有趣函数,对LeNet-5做了改动,也是对一些tf函数的实例化笔记吧。环境 Pycharm2019Python3.7.6tensorflow 2.0 话不多说,先放完整源码…

glassfish_多种监视和管理GlassFish 3的方法

glassfishGlassFish 3支持多种监视和管理方法。 在本文中,我将简要介绍GlassFish提供的管理,监视和管理方法。 GlassFish管理控制台 GlassFish基于Web的管理控制台GUI可能是GlassFish管理最著名的界面。 默认情况下,运行GlassFish后&#xf…

flask-mail异步发送邮件_SpringBoot 2.0 集成 JavaMail ,实现异步发送邮件

一、JavaMail的核心API1、API功能图解2、API说明(1)、Message 类:javax.mail.Message 类是创建和解析邮件的一个抽象类子类javax.mail.internet.MimeMessage :表示一份电子邮件。 发送邮件时,首先创建出封装了邮件数据的 Message 对象, 然后把…

Java 9中什么是私有的?

在进行面试时,我发现大多数应聘者都不知道Java中的private修饰符真正意味着什么。 他们知道一些足以进行日常编码的事情,但还远远不够。 这不成问题。 足够了解就足够了。 但是,了解Java的一些内部工作仍然很有趣。 在极少数情况下&#xff0…

java switch case怎么判断范围_【转】Java期末复习攻略!

期末19年就这样要过去了,终于到了小时候作文里的未来呢!然而,期末考试也随之来临了。不知大家“预习”的怎么样呢? 期末复习资料的放送快接近尾声了下面康康学长学姐们怎么教你们打java这个boss(下面是java大佬给大家的复习建议以…

spring aop示例_Spring JpaRepository示例(内存中)

spring aop示例这篇文章描述了一个使用内存中HSQL数据库的简单Spring JpaRepository示例。 该代码示例可从GitHub的Spring-JpaRepository目录中获得。 它基于带有注释的Spring-MVC-示例和此处提供的信息 。 JPA资料库 在此示例中,我们实现了一个虚拟bean&#xff1…

python人工智能入门优达视频_机器学习:优达教你搭建Python 环境的正确姿势

原标题:机器学习:优达教你搭建Python 环境的正确姿势为机器学习搭建好 Python 环境听起来简单,但有时候坑还不少。如果此前没有配置过类似的环境,很可能会苦苦折腾各种命令好几个小时。可是我明明只是想马上搞起来我的机器学习! 在…

java ee cdi_Java EE CDI ConversationScoped示例

java ee cdi在本教程中,我们将向您展示如何在Web应用程序中创建和使用ConversationScoped Bean。 在CDI中,bean是定义应用程序状态和/或逻辑的上下文对象的源。 如果容器可以根据CDI规范中定义的生命周期上下文模型来管理其实例的生命周期,则…

js input 自动换行_深入Slate.js - 拯救 ContentEditble

我们是钉钉的文档协同团队,我们在做一些很有意义的事情,其中之一就是自研的文字编辑器。为了把自研文字编辑器做好,我们调研了开源社区各种优秀编辑器,Slate.js 是其中之一(实际上,自研文字编辑器前&#x…