前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >影响Java调用性能有哪些因素

影响Java调用性能有哪些因素

作者头像
用户1289394
发布于 2018-02-26 09:19:32
发布于 2018-02-26 09:19:32
77400
代码可运行
举报
文章被收录于专栏:Java学习网Java学习网
运行总次数:0
代码可运行

影响Java调用性能有哪些因素

当时发生了什么?

这得从一个小故事说起。我在一个Java核心库的邮件列表中提交了一个修改 ——重写了一些本是 final 的方法。一石激起千层浪,这一改动引发了几番讨论。而其中一个讨论的话题是:调用一个去除 final 标记的方法,将导致哪种程度的性能下降(performance regression)。

我不能确定这一改变是否会导致性能下降,但当我决定将此暂时搁置一边,试着寻找在这个讨论里是否有人公布过任何相关的完整基准测试(sane benchmarks)时,结果空手而归。我不能肯定地说有关的基准测试是不存在的,或者说其他人没做过这方面的探讨。但我能肯定的是,在这里,连任何公开的代码评审都没有。唉,看来是时候写一个基准测试了。

基准测试的方法论

我决定选用一个相当不错的框架 —— JMH 来构建基准测试。如果你质疑它测试的准确性,那么建议你看下对这个框架作者(Aleksey Shipilev)的访谈,或者阅读一下由Nitsan Wakart撰写的一篇彰显此框架风采的博文。

现在,我想知道哪些因素影响了Java方法调用的性能。所以我决定以不同方式调用方法,并测算它们的性能开销。以单一变量为前提来构造一套基准测试,我便能逐个排除或确定,哪些因素或哪种组合会影响到方法调用的性能。

内联

方法调用的有无,是一个影响程度既是最高又是最低的因素——对于编译器来说,彻底优化方法调用所带来的开销并非不可能,有两种方法可以实现这样的需求:直接内联该方法本身和使用内联缓存(inline cache)。千万别被引入的这些术语给吓倒——它们都是通俗易懂的。现在我们假设有一个叫Foo的类,该类定义了一个叫bar的方法:

代码语言:javascript
代码运行次数:0
运行
复制
class Foo {
  void bar() { ... }
}

我们以如下的方式调用bar方法:

代码语言:javascript
代码运行次数:0
运行
复制
Foo foo = new Foo();
foo.bar();

这里有一个重要的知识点:实际调用 bar 的位置,即 foo.bar(),称为调用点(callsite)。当我们说一个方法“被内联”,意指方法体被插入到了调用点的位置上,以代替方法调用。对于那些由许多短小的方法所构成的程序——我称之为被适当分解的程序——内联可以有效地提升性能。这是因为结束以后可以发现,程序并没有把所有时间用在方法调用上,实际上程序并没有工作!我们在JMH中可以借由 CompilerControl 注释控制一个方法是否被内联。关于内联缓存的概念,我稍后再来说明。

层次结构深度与重写子类方法

如果我们移除一个方法的 final 关键字,便意味着我们能够重写它。所以这是另一个在进行测试我们需要考虑的情况。我会选择在同一层次结构中不同层次的子类里调用一些方法,并且在这些方法里有一些是会被不同层次的子类重写的。这样的测试能让我们确定或排除深的层次结构是否影响到重写所带来的性能开销。

多态性

先前我提到调用点这一概念时,我偷偷地回避了一个相当重要的问题——因为在子类中可以重写一个非 final 方法,这使得调用点可以调用不同的方法。现假设我传入一个 Foo 的实例或一个重写了 bar 子类—— Baz的实例,编译器如何得知要调用哪一个 bar 方法呢?在默认情况下,方法将在Java中被虚拟化(可重写)。对于任一调用点,编译器需要在一个称为虚拟表(vtable)的表中寻找与其对应的方法。这是个非常耗时的过程,所以,能进行优化的编译器,总是会试图减少这种查询带来的开销。一种方法就是先前提到的内联,这的确是个良策,但前提是编译器能证明在给定的调用点上调用的方法唯一。而这样的调用点我们称为单态(monomorphic)调用点。

不幸的是,进行这种分析需要耗费大量时间。所以在实际过程中,确定一个调用点是否单态是个不太可取的方法。对此,JIT编译器倾向于使用一种替代方法:列出哪些类可以在此调用点被调用,接着根据之前的N个相同的调用猜测此调用点是否是单态的。以假定某个调用点永远为单态,来进行投机性质的优化往往是可取的行为。因为这样的优化往往都是正确的,但也因它无法确保永远正确,编译器需要在方法调用之前注入一个用于检查方法类型的防护机制。

除了单态的调用点以外,还有两种调用点我们希望对其进行优化。一种称为双态(bimorphic)调用点,在该点上有两个候选方法。对此你依然可以实现内联——借助防护代码,让其检测应调用哪一个方法,并引导程序跳转至内联在调用点的两个方法体中真正对应的那一个。这样的方式还是比查看所有虚拟表的方式要快得多。但在某些情况下,我们得利用内联缓存来进行优化。内联缓存需要借助一张特定的跳转表( jump table),这种表类似于对虚拟表查找做的一份缓存。hotsopt JIT编译器支持双态内联缓存,并定义那些拥有三个及三个以上候选方法的调用点为超多状态(megamorphic)调用点。

这就使得我在基准测试与探究当中,需要额外地把调用情况划分为三类:单态、双态、超多状态。

结果

让我们把结果分类组织,以便研究细节。我已经提供了统计产生的原始数据。但我们的兴趣点不应放在性能测试结果的具体数值上,而应是不同类型的方法调用的性能开销之间的比率以及各自的错误率是否够低。如果最快与最慢的结果之间比率为6.26,则说明这是一个显著性差异。由于测试时使用的是空方法(详见源代码),所以在实际应用中,这样的差异会更大。

你可以在 github上查看此次基准测试的源代码。为了避免产生困惑,待会所有的结果将分块显示。最后显示的多态的基准测试是在 PolymorphicBenchmark 类中进行,其它的则在 JavaFinalBenchmark 类中。

简单调用点

最先看到的的一组结果,是比较调用一个 virtual 方法、一个 final 方法和一个拥有很深的层级结构,同时被所有子类重写的方法所带来的开销。注意,调用这些方法的时候我们都强制编译器不要内联它们。我们可以看到:三者在时间花费上相差甚微,并且各自的误差率都小到可以忽略。对此我们可以断定,仅添加一个 final 关键字并不会大幅度提升调用性能,重写一个方法也不见得会带来什么影响。

内联简单调用

现在,我们在开启内联的情况下再来一次相同的测试。由结果可见,final 方法和 virtual 方法的时间花费依旧相近,并比在没有内联的情况下快了4倍,我将此归功于内联优化。相比而言,被所有子类重写的方法的结果可就没那么好看了。我推测这是由于此方法有多个子类实现,使得编译器必须插入一个类型保护。有关的细节我们将在研究多态性的结果时进行阐述。

类层次结构的影响

哇噢——这儿有好几个的方法!方法名称的编号(1~4)代表该方法调用的层次。因此,parentMethod4 表示我们调用的方法位于class的上面第四级。(译注:在源代码中该方法位于顶层的父类)。由此结果我们能断定,结构层次的深度对性能开销没有影响。在开启内联的实例中,结论也是一样。这个测试中,被内联的方法的性能与 inlinableAlwaysOverriddenMethod 相当,但稍逊于 inlinableVirtualInvoke。我依旧认为这与使用了类型保护有关。事实上JIT编译器能剖析所有候选方法,从而只内联对应的那一个,但这并不证明它总会这么干。

类的层级结构对final方法的影响

该测试的结论与第一个测试一样 —— final 关键字不会产生任何影响。我本以为该测试将证明 inlinableParentFinalMethod4 以无类型保护的方式进行内联,但结果表明事实并非如此。

多态性

最后,我们来看涉及多态分派(polymorphic dispatch)的测试结果。单态调用的性能开销与之前virtual方法相近。但对于双态与超多状态调用,由于需要在一张较大的虚拟表上面进行查找,所以需要更多的时间。而一旦我们开启内联支持,类型分析(type profiling )将会在单态或双态的调用点启用,使得在这些调用点上的方法调用的开销减少。但与层级结构的实例一样,这只会减少少量的时间。相比而言,超多状态的实例则依旧耗时较长。记住,我并没有说在这个测试中hotspot禁用了内联,它只是没有实现多态调用点的多态内联缓存。

我们从中学到了什么?

我认为,需要我们引起注意的是,很多人没有认识到不同方式的方法调用所花费的时间是不一样的。即便有些人发现了这种问题,但他们不去证明是否真的如此。作为第一个吃螃蟹的人,我列出了各种坏的假设,因此我希望这份研究能够帮助到大家。以下是我很乐于与大家分享的一些结论:

  • 最快与最短的方法调用的类型之间存在巨大的性能差别。
  • 在实际应用中,添加或删除final关键字并不会真正影响性能。但如果除此以外,你还在层级结构上进行某些操作,那这些行为则可能导致性能下降。
  • 更深的类的层次结构并不会真正影响到调用的性能。
  • 单态调用比双态调用更快。
  • 双态调用比超多状态调用更快。
  • 我们在能够进行剖析(profile-ably),但是不能进行查验的单态调用点中看到类型保护,这种保护会使得这些调用点的调用性能低于那些能够进行查验的单态调用点。

我想说的是,对我而言,类型保护带来的性能开销是一个“重大发现”。这是一个我之前很少提及,并且总是当做无关事物忽视掉的因素。

注意事项与进一步工作

本文不能囊括这个话题的全部内容。因为:

  • 这篇博文所关注的影响到方法调用的性能的因素,只与类型有关。所以,有一个因素我并未提及:方法的长短或者说调用栈的深度——如果方法太长,那么它将不会被内联,为此你必须承受方法调用所带来的开销。另外,为了使代码具有易读性,你也应当把方法写得短小一些。
  • 在本次测试的所有我并没有尝试引入接口。如果你对此有兴趣的话,这里有一篇有关接口调用的性能的研究Mechanical Sympathy。
  • 还有一个因素被我完全忽视了,那就是方法内联的优化方式在不同编译器上的效果差异。当编译器是仅关注某个方法(内部过程优化)时,它们需要足够地信息才能有效优化。内联的限制可以有效地减少其它优化所需要关注的范围。
  • 试着站在汇编语言的层面进行解释的话,会涉及更多的细节内容。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2015-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习网 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
数据链路层学习之LLDP「建议收藏」
随着网络技术的发展,接入网络的设备的种类越来越多,配置越来越复杂,来自不同设备厂商的设备也往往会增加自己特有的功能,这就导致在一个网络中往往会有很多具有不同特性的、来自不同厂商的设备,为了方便对这样的网络进行管理,就需要使得不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息。 LLDP(Link Layer Discovery Protocol,链路层发现协议)就是用于这个目的的协议。LLDP定义在802.1ab中,它是一个二层协议,它提供了一种标准的链路层发现方式。LLDP协议使得接入网络的一台设备可以将其主要的能力,管理地址,设备标识,接口标识等信息发送给接入同一个局域网络的其它设备。当一个设备从网络中接收到其它设备的这些信息时,它就将这些信息以MIB的形式存储起来。 这些MIB信息可用于发现设备的物理拓扑结构以及管理配置信息。需要注意的是LLDP仅仅被设计用于进行信息通告,它被用于通告一个设备的信息并可以获得其它设备的信息,进而得到相关的MIB信息。它不是一个配置、控制协议,无法通过该协议对远端设备进行配置,它只是提供了关于网络拓扑以及管理配置的信息,这些信息可以被用于管理、配置的目的,如何用取决于信息的使用者。
全栈程序员站长
2022/07/21
1.4K0
数据链路层学习之LLDP「建议收藏」
图解OSI七层模型,2024最强科普!
公众号后台有小伙伴让我讲一下OSI七层模型,这个概念以前其实讲过很多遍了,而且属于网络技术领域中最基本的概念,虽然是最基本的概念,在看的各位有谁能够说“我精通OSI”的?
网络技术联盟站
2025/01/15
2.4K0
图解OSI七层模型,2024最强科普!
一文读懂TSN
由于以太网的普及度高,针对以太网的实时应用衍生出多种技术。2001年,贝加莱推出了工业领域应用的以太网POWERLINK;2003年,Siemens基于Profibus开发了PROFINET;Rockwell、ABB基于DeviceNet开发了Ethernet/IP;Beckhoff 开发了EtherCAT;Rexroth基于SERCOS开发了SERCOSIII。这些实时以太网通常采用HUB透传的方式,不支持交换机。实时以太网与标准以太网无法在同一网络中进行数据传输。
SDNLAB
2021/12/01
11.3K0
一文读懂TSN
IEEE 802.1标准简介
IEEE 802.1是一组协议的集合,如生成树协议、VLAN协议等。为了将各个协议区别开来,IEEE在制定某一个协议时,就在IEEE 802.1后面加上不同的小写字母,如IEEE 802.1a定义局域网体系结构;IEEE 802.1b定义网际互连,网络管理及寻址;IEEE 802.1d定义生成树协议;IEEE 802.1p定义优先级队列;IEEE 802.1q定义VLAN标记协议;IEE 802.1s定义多生成树协议;IEEE 802.1w定义快速生成树协议;IEEE 802.1x定义局域网安全认证等。
利又德智能感知
2022/11/07
1.3K0
异构协议融合实践:稳联技术Profinet转EtherCAT网关与西克传感器的协同配置解析
*客户*:王工,在工业自动化场景中,西门子PLC与西克传感器因协议差异存在通讯壁垒,此次采用稳联技术Profinet转EtherCAT网关实现集成的核心需求是什么?
稳联技术老杨
2025/05/28
810
异构协议融合实践:稳联技术Profinet转EtherCAT网关与西克传感器的协同配置解析
[大佬] 重新认识PROFINET 01
在《西门子PLC通信原理探秘》系列的第一篇故事中,我提到PROFINET是我在西门子技术支持生涯的起点,由于RPOFINET我开始接触以太网,理解协议,堆栈,帧,交换机,IWLAN,实时等诸多的概念,也正是探索这些概念,通过一个现场诊断的契机,我逐步完善了自认为已经掌握的PLC通信的知识,形成了以时间片和CCP为基础的PLC通信体系,真正深入到PLC内部通信-数据交换和数据处理。还有交换机技术,作为PROFINET网络中重要的有源部件,其作用不言而喻,其概念的理解有助于理解PROFINET的工作机制。甚至后来的一连串的连锁和外延知识,例如PKI(Public Key Infrastructure),这是未来,例如在使用云等APP与现场应用之间流行的加密方式,这些知识都是源于这个起点。所以在这个专栏的起点我也选择从PROFINET开始,一步步一点点给大家展示相关PROFINET的奥秘,希望从一些独特的角度给大家刷新对这些知识的认知。
科控物联
2022/03/29
1.3K0
基于TIA Portal组态软件PROFINET通讯搭建
PROFINET智能设备(I Device)功能使CPU不但可以作为一个智能处理单元处理生产工艺的某一运行过程,而且可以和IO控制器之间交换过程数据。智能设备功能简化了与IO控制器的数据交换以及对CPU的操作。智能设备可作为IO设备链接到上层IO控制器。
IRTeam-工业安全
2022/05/10
2.4K0
基于TIA Portal组态软件PROFINET通讯搭建
浅谈 Linux 内核无线子系统
Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高层次的问题。 跟踪探索了一段时间的源代码后,我写下了这篇总结,希望在 Linux 无线网络的工作原理上,读者能从这篇文章获得一个具有帮助性的概览。
RainMark
2019/09/04
3.6K0
浅谈 Linux 内核无线子系统
S7-1200作为智能IO设备和S7-300的Profinet 通信
S7-1200 V4.0 支持智能 IO 设备功能,故可使用 S7-1200 作为智能 IO 设备和 S7-300 PN CPU 的 Profinet 通信。本例中将 S7-300 做为控制器,连接作为智能 IO 设备的S7-1200 CPU 实现 Profinet 通信;下面详细介绍使用方法。
科控物联
2022/03/29
2.5K0
S7-1200作为智能IO设备和S7-300的Profinet 通信
无线网络嗅探中的Radiotap
引言 在WLAN无线网络抓包的时候不管是用wireshark、tcpdump还是scapy都会出现Radiotap、LLC、SNAP协议层。 如图所示: LLC逻辑逻辑链路控制子层(包括SNAP)和M
FB客服
2018/02/27
2.7K0
无线网络嗅探中的Radiotap
【连载】老曹带你做大神之走进奇妙的工控世界(四)
每周二,由老曹带领大家一步一步进入到自动化控制系统的奇妙世界;一步一步告诉大家如何从一张P & ID(Piping and Instrumentation diagram)图设计一套PLC控制系统,其中包括:IO点统计、PLC选型、IO表设计、IO端子图设计、机柜布置图设计、总线设计、网络设计、PLC程序编制、上位机画面编制、文档制作;让你轻轻松松完整以PLC为主控制器的控制系统设计。
剑指工控
2021/11/09
1.1K0
5G和PROFINET实现工业4.0的实时通信与自动化
多年来,PROFINET一直是全球众多自动化系统的重要组成部分。然而,随着工业5G网络的引入,行业面临一个挑战:如何将现代5G网络的优势与已建立的PROFINET通信结合起来,并在数百个分散的接口和设备上高效地集中管理?
Hello工控
2024/09/06
2660
5G和PROFINET实现工业4.0的实时通信与自动化
IIoT小课堂 | 智慧互联篇 (答疑与实操大全)
我们通过PLC联网从PLC里获取一些生产的重要数据以及设备的状态信息(目前PLC和HMI多数还是独立运行)
剑指工控
2021/11/09
8670
LLDP协议、STP协议 笔记
随着网络技术的发展,接入网络的设备的种类越来越多,配置越来越复杂,来自不同设备厂商的设备也往往会增加自己特有的功能,这就导致在一个网络中往往会有很多具有不同特性的、来自不同厂商的设备,为了方便对这样的网络进行管理,就需要使得不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息。
全栈程序员站长
2022/09/05
1.6K0
LLDP协议、STP协议 笔记
你知道Profinet CC(Conformance Classes)三个等级吗?
Profinet CC,其中CC是一致性等级(Conformance Classes)的简写,它是一种基于应用需求的,对Profinet系统的功能的分类分级标准。CC分类标准不仅仅规定了Profinet设备至少需有哪些功能,可选哪些功能,更重要的是,如果一台设备要取得Profinet认证,必须要选择一个CC等级进行相应的功能测试,通过后才能取得相应CC等级的认证证书。对于使用者,CC同样也能为工程的选型提供了快速有效的指引。
Hello工控
2024/08/29
5740
你知道Profinet CC(Conformance Classes)三个等级吗?
S7-1200 之间 Profinet IO 通信
CPU 的"I-Device"(智能设备)功能简化了与 IO 控制器的数据交换和 CPU 操作过程(如用作子过程的智能预处理单元)。智能设备可作为 IO 设备链接到上位IO 控制器中,预处理过程则由智能设备中的用户程序完成。集中式或分布式(PROFINET IO 或 PROFIBUS DP)I/O 中采集的处理器值由用户程序进行预处理,并提供给 IO 控制器。
科控物联
2022/03/29
4.5K0
S7-1200 之间 Profinet IO 通信
交换机中的冗余链路管理
许多交换机或交换机设备组成的网络环境中,通常使用一些备份连接,以提高网络的健全性,稳定性。备份连接也叫备份链路,冗余链路等。
利又德智能感知
2022/11/08
1.1K0
交换机中的冗余链路管理
WiFi安全漏洞KRACK深度解读
前段时间爆出的WiFi安全漏洞KRACK,全球的WLAN设备无一幸免,也就是说wifi用户连接网络,不论是在公司,家里,还是咖啡馆,都有可能遭受攻击,问题是这只是发现了一个,还有没有发现的,也许还有更严重的问题,又该怎么办呢,尤其是那些协议层面的安全隐患,普通群众恐怕也束手无策。今天偶然看到一篇文章,文章对KRACK事件的技术缘由的进行了一番梳理剖析,纯技术系风格,看完后对此次爆出的安全漏洞有了一个比较全面的了解,更意识到网络安全问题刻不容缓,感谢作者。
全栈程序员站长
2022/06/28
1.7K0
WiFi安全漏洞KRACK深度解读
爱情就像 STP 生成树协议,哪怕有再多的干扰和潜在分歧,我们也能找到最优路径,让爱稳稳流动,不会陷入迷茫的循环——基于华为ENSP的STP生成树协议深入浅出(1)
盛透侧视攻城狮
2025/02/12
1570
爱情就像 STP 生成树协议,哪怕有再多的干扰和潜在分歧,我们也能找到最优路径,让爱稳稳流动,不会陷入迷茫的循环——基于华为ENSP的STP生成树协议深入浅出(1)
网工上午经典考题汇总---必记
香农定理总结出有噪声信道的极限数据速率:在一条带宽为W(HZ),信噪比为S/N的有噪声信道的极限数据速率Vmax为:
网络技术联盟站
2019/08/06
2.1K0
网工上午经典考题汇总---必记
推荐阅读
相关推荐
数据链路层学习之LLDP「建议收藏」
更多 >
目录
  • 影响Java调用性能有哪些因素
    • 当时发生了什么?
    • 基准测试的方法论
    • 内联
    • 层次结构深度与重写子类方法
    • 多态性
    • 结果
    • 简单调用点
    • 内联简单调用
    • 类层次结构的影响
    • 类的层级结构对final方法的影响
    • 我们从中学到了什么?
    • 注意事项与进一步工作
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档