性能文章>相同版本JVM和Java应用,在X86和AArch64平台性能相差30%,何故?>

相同版本JVM和Java应用,在X86和AArch64平台性能相差30%,何故?原创

235305

编者按:目前许多公司同时使用X86和Aarch642种主流的服务器。但是相同版本的JVM和Java应用,相同的参数JVM参数,但是应用性能在不同的平台中表现相差30%,X86远好于Aarch64平台。本文分析了一个应用在Aarch64性能下降的例子,发现JVM的CodeCache大小是引起这个性能问题的根源,进而研究什么导致了不同平台上Codecache大小的不同。最后笔者给出了不同平台中该如何设置参数规避该问题。希望本文能给读者一些启示:当使用不同的硬件平台时需要关注底层硬件对于上层应用的影响。

业务在x86和arrach64上同时部署时(相同的JDK和Java应用版本),发现aarch64平台性能下降严重问题。进一步查看日志,发现在aarch64平台中偶有如下情况:
image.png
这代表JVM中的CodeCache满了,导致编译停止,未编译的方法只能解释执行,进而严重影响应用性能。什么是CodeCache?

CodeCache是什么

简单来说,CodeCache用于存放编译后的方法,主要分为三部分:
1.Non-nmethods:包括运行时Stub,Adapter等。
2.Profiled nmethod:包括会采集信息的方法,即分层编译中第2、3层的方法
3.Non-Profiled nmethods:包括不采集信息的方法,即分层编译中第1、4层的方法,也包括JNI的方法
注:分层编译指的是JVM同时存在C1和C2两种编译器,C1做一些简单的编译优化,耗时较短,C2做更多复杂的编译优化,性能较好,编译耗时较多。分层编译的触发在JVM内存会根据相应的条件进行触发,关于更多分层编译相关知识可以参考官网。
在JDK9之后[1],这些会分配到不同的区域(使用不同区域的优点:查找、回收等),JDK8中会分配到同一块区域。
JVM平时会清理一些不可达的方法,例如由于退优化等产生的死方法,另外UseCodeCacheFlushing选项(默认开启),还会清理较老以及执行较少的方法。一旦CodeCache满了之后,会停止编译,直到CodeCache有空间,若关闭了UseCodeCacheFlushing选项,则会直接永久停止编译。
不同的JVM版本以及不同的参数,默认的CodeCache大小不同。JDK11中默认参数下大小为240M,若想获取(确认)默认情况下的CodeCache大小,建议使用-XX:+PrintFlagsFinal选项获取ReservedCodeCache的大小。
CodeCache大小主要通过以下选项调节:

Option Description
InitialCodeCacheSize 初始的CodeCache大小(单位字节)
ReservedCodeCacheSize 预留的CodeCache大小,即最大CodeCache大小(单位字节)
CodeCacheExpansionSize CodeCache每次扩展大小(单位字节)

使用–XX:+PrintCodeCache选项可以打印应用使用的CodeCache情况,如下:
image.png
其中max_used表示应用中使用到的CodeCache大小,据此可以设置合适的ReservedCodeCacheSize值。

AArch64 vs x86_64

我们都知道aarch64和x86分别为RISC和CISC架构,因此代码密度方面存在一定差异,在这篇文章[2]中比较了不同指令集下手写汇编的大小,可以看到aarch64的代码密度是RISC架构中较优的,但相比x86_64仍稍差些(其中RISC最差,m68k最好)。

image.png
另外笔者选用业界通用的java测试套dacapo[3]比较aarch64和x86_64下codecache占用的大小。
image.png
可以看到,在aarch64架构下,CodeCache均比x86_64要大,但根据不同场景,大小差距不同,在5%-20%之间。因此在我们发现相同应用在x86和aarch64上时,CodeCache大小需要进行相应的调节。
除此之外,还需要注意InlineSmallCode选项,JVM只会inline代码体积比该值小的方法。JVM通过inline可以触发更多的优化,因此inline对于性能提升也很重要。在JDK11中,InlineSmallCode在x86下的默认值为2000字节,在aarch64下的默认值为2500字节。而JDK8中,InlineSmallCode在x86和aarch64下默认值均为2000字节。因此建议迁移时也相应修改InlineSmallCode的值。

[1] https://bugs.openjdk.java.net/browse/JDK-8015774
[2] http://web.eece.maine.edu/~vweaver/papers/iccd09/ll_document.pdf
[3] http://dacapobench.org/

后记

如果遇到相关技术问题(包括不限于毕昇JDK),可以进入毕昇JDK社区查找相关资源(点击原文进入官网),包括二进制下载、代码仓库、使用教学、安装、学习资料等。毕昇JDK社区每双周周二举行技术例会,同时有一个技术交流群讨论GCC、LLVM、JDK和V8等相关编译技术,感兴趣的同学可以添加如下微信小助手,回复Compiler入群。
image.png

点赞收藏
分类:标签:
毕昇JDK社区

毕昇JDK是华为基于OpenJDK定制的开源版本,是一款高性能、可用于生产环境的OpenJDK发行版。

请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

记一次线上RPC超时故障排查及后续GC调优思路

记一次线上RPC超时故障排查及后续GC调优思路

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

【全网首发】从源码角度分析一次诡异的类被加载问题

【全网首发】从源码角度分析一次诡异的类被加载问题

5
0