编者按:在升级 jdk8u 的小版本后(从 8u74 升级到 8u202),遇到性能剧烈下降的问题(性能下降 13 倍)。该应用是一个非常简单的 web 应用,且应用在 jdk 升级前后并无任何发布修复。
通常来说 jdk 小版本升级都是问题修改,不影响功能和性能使用,而应用性能剧烈下降一定是 jdk 的内部 bug。对于这样明确由 jdk 引起的性能问题,该如何解决?
最常见的方法是通过工具分析 jvm 执行过程,检查函数执行的情况是否发生变化,如果找到变化,则可以深入分析哪些因素引起了变化,并进一步得到根因。笔者使用 perf 工具分析 jvm 执行时的热点函数,并对出现问题的函数进行剖析,使用函数插桩来分析函数的执行次数,发现不同版本行为差异的根源,并找到了引起问题的根因。希望读者遇到性能问题时可以参照本文使用 perf 工具对问题进行定位。
工欲善其事,必先利其器。程序员在定位性能瓶颈的时候,要是有一个趁手的性能调优工具,能一针见血地指出程序的性能问题,可谓事半功倍。
linux 中最常用的性能调优工具 perf(linux 系统原生提供的性能分析工具),使用 perf 先对应用(假设要采样的应用为 javaapp)进行采样,使用 record 命令,如下:
perf record java javaapp
另外 perf 能按出现的百分比降序打印 cpu 正在执行的函数名以及调用栈,如命令:
perf report -n
这种结果的输出还是不直观的,linux 性能优化大师 brendan gregg 发明了火焰图(因整个图形看起来像燃烧的火焰而得名),以全局的方式来看各个函数的调用时间分布,以图形化的方式列出调用栈。
火焰图是基于 perf 的结果生成的图形,我们先了解一下怎么去看火焰图。
x 轴表示被抽样到的次数。理解 x 轴的含义,需先了解采样数据的原理。perf 是在指定时间段内,每隔一段时间采集一次数据,被采集到的次数越多,说明该函数的执行总时间长,可能的原因有:调用次数多,或者单次执行时间长。因此,x 轴的宽度不能简单的认为是运行时长。y 轴表示调用栈。
如何从火焰图看出性能的瓶颈在哪里?最有理由怀疑的地方,顶层的“平顶”。关于 perf 和火焰图使用方法可以参官网http://www.brendangregg.com/flamegraphs/cpuflamegraphs.html。
下面是我们利用火焰图来定位问题的一次实战。
火焰图定位问题的实战
问题场景
问题发生的场景是客户端向服务器发起 http 请求,服务器返回数据给客户端(这是一个非常简单的服务交互)。我们发现使用 jdk 8u74 的性能要远优于 jdk 8u202 的性能,下表中统计了 20 次服务器的响应时长。
从响应时间来看,8u202 相比 8u74 性能下降 13 倍之多,由于应用本身并未做任何修改,所以考虑使用火焰图来定位性能消耗的问题点。在 8u74 和 8u202 分别运行应用,并用 perf 的 record 抓取数据并生成火焰图。
火焰图定位
对比两张火焰图,使用 8u74 时 clienthandshaker.processmessage 占比为 1.15%,而在 8u202 中这个函数占比为 23.98%,很明显在 clienthandshaker.processmessage 带来了性能差异。
根因定位
两者在这个 clienthandshaker.processmessage 上的 cpu 消耗差异很大,继续分析这个函数找到根因。
void processmessage(byte handshaketype, int length) throws ioexception { if(this.state >= handshaketype && handshaketype != 0) { //... 异常 } else { label105: switch(handshaketype) { case 0://hello_request this.serverhellorequest(new hellorequest(this.input)); break; //... case 2://sever_hello this.serverhello(new serverhello(this.input, length)); break; case 11:///certificate this.servercertificate(new certificatemsg(this.input)); this.serverkey = this.session.getpeercertificates()[0].getpublickey(); break; case 12://server_key_exchange 该消息并不是必须的,取决于协商出的key交换算法 //... case 13: //certificate_request 客户端双向验证时需要 //... case 14://server_hello_done this.serverhellodone(new serverhellodone(this.input)); break; case 20://finished this.serverfinished(new finished(this.protocolversion, this.input, this.ciphersuite)); } if(this.state < handshaketype) {//握手状态 this.state = handshaketype; } } }
processmessage()主要是通过不同的信息类型进行不同的握手消息的处理。而在火焰图中可以看到,jdk8u74 图中,主要消耗在函数 serverfinished()和 serverhello()上,而 jdk8u202 主要消耗在函数 serverhellodone()和 serverkeyexchange()。
在介绍火焰图的时候,我们有提到,x 轴的长度是映射了被采样到的次数。因此需要进一步确定消耗:函数单次执行耗时过长而成为热点,还是因为频繁调用函数导致函数耗时过长而成为热点。
可通过字节码插桩(通过 instrument 技术实现对函数的计数,然后编译成 agent,执行应用时加载 agent,具体使用 instrument 的方法可以参考官方文档)查看函数 serverhellodone()的调用次数及执行时间。
jdk8u202 数据 execute count : 253 execute count : 258 execute count : 649 execute count : 661 serverhellodone execute time [1881195 ns] execute count : 1223 execute count : 1234 execute count : 1843 execute count : 1852 serverhellodone execute time [1665012 ns] execute count : 2446 execute count : 2456 serverhellodone execute time [1686206 ns] jdk8u74 数据 execute count : 56 execute count : 56 execute count : 56 execute count : 56 execute count : 56 execute count : 56
execute time 是取了每 1000 次调用的平均值,execute count 每 5000ms 输出一次总执行次数。很明显使用 jdk8u202 时在不断调用 serverhellodone,而 74 在调用 56 次后没有再调用过这个函数。
初始化握手时,serverhellodone 方法中,客户端会根据服务端返回加密套件决定加密方式,构造不同的 client key exchange 消息;服务器如果允许重用该会话,则通过在 server hello 消息中设置相同的会话 id 来应答。
这样,客户端和服务器就可以利用原有会话的密钥和加密套件,不必重新协商,也就不再走 serverhellodone 方法。从现象来看, jdk8u202 没有复用会话,而是建立的新的会话。
水落石出
查看 jdk8u 161 的 release notes,添加了 tls 会话散列和扩展主密钥扩展支持,找到引入的一个还未修复的 issue,对于带有身份验证的 tls 的客户端,支持 useextendedmastersecret 会破坏 tls-session 的恢复,导致不使用现有的 tls-session,而执行新的 handshake。
jdk8u161 之后的版本(含 jdk8u161),若复用会话时不能成功恢复 session,而是创建新的会话,会造成较大性能消耗,且积压的大量的不可复用的 session 造成 gc 压力变大;如果业务场景存在不变更证书密钥,需要复用会话,且对性能有要求,可通过添加参数-djdk.tls.useextendedmastersecret=false 来解决这个问题。
后记
如果遇到相关技术问题(包括不限于毕昇 jdk),可以通过毕昇 jdk 社区求助。毕昇 jdk 社区每双周周二举行技术例会,同时有一个技术交流群讨论 gcc、llvm 和 jdk 等相关编译技术,感兴趣的同学可以添加如下微信小助手入群(请备注:complier)。
利用三相交流和线圈制造旋转磁场的方法
精彩出炉 | ECS2023第五届中国电子通信与半导体CIO峰会圆满落幕!
摇摄/MPEG-7,摇摄/MPEG-7是什么意思
LPC8N04及LPC8N04开发板主要特性PCB设计图
略谈MSP43单片机端口
如何解决JDK8小版本升级后性能下降的问题
PCB铜箔厚度单位为什么是盎司
如何通过区块链技术促进贸易高质量的发展
amazon亚马逊英国站
刷脸时代来了 支付宝人脸识别技术进医院
全球各大芯片巨头高管齐聚旧金山SEMICON West讨论分享各自观点
常用电子元器件及其在电路中的作用
靶式流量计的流量公式
工控机服务器能通用吗_服务器和工控机有什么区别
出海有“云”!华为云全球加速助力跨国企业提升网络体验
高光谱遥感获取伐区调查数据的应用综述
飞秒激光辅助制造石英晶体MEMS谐振器
以材质划分网线分为哪几类
摩根大通:Phone12销量预期过于乐观,重申对苹果的“增持”评级
YouGov:只有28%的美国人认为自动驾驶很安全