作者|阿里云智能事业群 高级技术专家 陆传胜
阿里巴巴有着最丰富的 Java 应用场景,覆盖电商,金融,物流等众多领域,是世界上最大的 Java 用户之一。 2019 年 3 月 21 日,阿里巴巴在北京阿里云峰会上正式宣布开源了 Alibaba Dragonwell 8 产品,并建立了 Alibaba Dragonwell 社区来为全球 Java 用户,特别是中文社区的 Java 用户提供长期支持的 JDK 产品。自宣布开源以来,Alibaba Dragonwell 8 受到了国内外 Java 开发者的关注,今天这篇文章就来详解 Alibaba Dragonwell8 的快速安装和使用,同时列出了参与社区建设的几种方式,期望为那些即将安装及使用 Alibaba Dragonwell 8 的开发者提供参考。
Alibaba Dragonwell 8 介绍
Alibaba Dragonwell 8 是一款免费的 OpenJDK 发行版。它提供长期支持,包括性能增强和安全修复。Alibaba Dragonwell 8 目前支持 X86-64/Linux 平台,在数据中心大规模 Java 应用部署情况下, 可以大幅度提高稳定性、效率以及性能。Alibaba Dragonwell 8 是 OpenJDK 的下游(friendly fork),使用了和 OpenJDK 一样的 licensing。Alibaba Dragonwell 8 与 Java SE 标准兼容,用户可以使用 Alibaba Dragonwell 8 开发和运行 Java 应用程序。此次开源的 Alibaba Dragonwell 8 是阿里巴巴内部 OpenJDK 定制版 AJDK 的开源版本, AJDK 为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,100,000+ 服务器的阿里巴巴数据中心。
安装 Alibaba Dragonwell 8
目前 Alibaba Dragonwell 8 只支持 Linux x86-64 平台,并且提供了二进制的预编译 JDK 包,您可以通过下面的简单两步安装 Alibaba Dragonwell 8。
- 从 Github 上面 Alibaba Dragonwell 8 项目的下载页面下载预编译的二进制 JDK 包。
下载页面链接 https://github.com/alibaba/dragonwell8/releases - 将下载下来的 tar 包解压到目标安装目录即可。
安装完毕后,只需要将应用引用 的 JAVA_HOME 指向 Alibaba Dragonwell 8 的安装目录就可以使用了。我们以Tomcat8.5.39 版本为例,为了让 Tomcat 运行在 Alibaba Dragonwell 8上面,只需要在启动Tomcat时使用如下命令:
JAVA_HOME=/path/to/dragonwell8/installation sh tomcat/bin/catalina.sh start
为了确认是运行在 Alibaba Dragonwell 8上面,可以进一步通过给 java 命令添加 -showversion
参数来打印 JDK 版本信息加以判断。
JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-showversion" sh tomcat/bin/catalina.sh start
启动完毕后在 tomcat/logs/catalina.out 文件的开头,可以看到 Alibaba Dragonwell 8 的版本信息
使用 Alibaba Dragonwell 8 专有特性
在 8.0-preview 这个版本中, Alibaba Dragonwell 8 提供了两个在阿里巴巴的生产环境中进行过广泛验证的特性:JWarmUp 和 Java Flight Recorder。这两个特性都已经在上游 OpenJDK 社区提交了 JEP 或 patch,在上游合并完成之前,我们希望让 Alibaba Dragonwell 8 的用户可以提前使用到这两个特性。
JWarmUp 快速预热 Java 应用
OpenJDK 使用了 JIT(Just-in-time) 即时编译技术,可以动态的把 Java 字节码编译成高度优化过机器码,提高执行效率,但在编译之前,Java 代码是以相对低效的解释器模式执行的。
在应用启动完成后、业务流量刚进来的短时间内,容易出现的状况是大量 Java 方法开始被 JIT 编译,同时业务请求被较慢的解释器模式执行,最终的结果就是系统负载飙高,可能导致很多用户请求超时。为了解决这个问题,之前的很多做法是使用模拟流量来提前预热应用,JWarmUp 特性提供了一个新的选择,就是利用 Java 虚拟机前一次执行编译得记录来预热本次应用的执行。
JWarmUp 的原理如下图所示:
一个典型的应用场景是当应用需要发布新版本的时候,
- 首先 JWarmUp 在 Beta 环境(或者有着和生产环境类似流量的其他场景)的单台机器上短时间执行 Java 应用,并记录、收集这段时间里面 JIT 编译器所做动作的一些元数据。
- 然后会把这些元数据复制到生产环境的每一台包含了新版本代码的机器/容器里面。
- 最后在生产环境机器中通过 JWarmUp 参数加载 beta 环境生成的元数据,来指导生产环境的机器在启动应用的过程中就完成 JIT 预热。
这样当用户请求进入的时候,应用就会处于性能最高的峰值状态。
收集预热数据
还以 Tomcat 应用为例,可以添加下面的命令行参数来在 beta 环境中收集 JIT 编译时生成的元数据,其中参数 -XX:CompilationWarmUpLogfile=
指定的就是生成的 JWarmUp 文件的路径。
JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUpRecording -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start
生成之后可以把这个文件通过 OSS、SFTP 等方式传输到生产环境的机器上。
使用记录的数据来预热 Java 应用
在生产环境的机器上,只需要使用下面的参数,就可以利用之前的预热数据来启动一个新的 Tomcat 实例,其中参数 -XX:CompilationWarmUpLogfile=
制定的就是需要被加载的 JWarmUp 文件路径,这个文件应该是上一步收集预热数据时从 beta 环境复制过来的。
JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start
使用 Java Flight Recorder 分析 Java 应用性能
JFR(Java Flight Recorder)是 JVM 内置的基于事件的性能分析特性,这是 Oracle JDK7u4 版本开始提供的商业特性,2018 年的时候在 JDK11 上开源了这个特性,但是一直没有针对 JDK8 版本的支持。
阿里巴巴和 RedHat、Azul、Amazon 等公司一起合作尝试把这个特性移植回 JDK8上,不过该 patch 暂时还没有合并回 OpenJDK8u仓库,我们在 Alibaba Dragonwell 8 中提供了 Alibaba 移植的 JFR 版本,用于帮助用户提前获取这方面的支持。
JFR 的用法很简单,用户使用命令行参数或者 jcmd 命令控制 HotSpot 输出性能数据到文件中,然后就能使用开源的 jmc 工具在图形界面中打开、分析生成的数据文件了。
使用 JFR 收集性能数据
在 Alibaba Dragonwell 8中,默认情况下 JFR 特性是处于关闭的状态,必须添加命令行参数 -XX:+EnableJFR
来允许使用 JFR 特性。 Alibaba Dragonwell 8 提供了不同的方式来使用 JFR 采集性能数据。
应用可以可以通过命令行参数指定 JFR 再启动后立马开始采集数据,这个对于诊断启动阶段的问题会很有帮助。下面的示例命令会在 Java 进程的 JFR 模块初始化后就开始收集 JFR 数据,持续一分钟,并且把数据都输出到名为rec.jfr 的文件中。
JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR -XX:StartFlightRecording=duration=1m,filename=rec.jfr" sh bin/catalina.sh start
应用也可以只添加 -XX:+EnableJFR
,然后通过 jcmd
命令在应用启动后的任意时间点开始采集数据,这种情况更加灵活可控,可以满足随时随地进行分析的需求。
以 Tomcat 为例,启动 Tomcat 的命令可以是:
JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR" sh bin/catalina.sh start
当需要收集数据进行分析时,只需要使用目标 Tomcat 进程的 PID 来执行 JFR 对应的 jcmd 命令即可。以 Tomcat 为例,如果需要在指定时刻开始收集 10 秒的数据,那么触发的命令如下:
$ ps ax | grep tomcat
77522 pts/18 Sl+ 0:08 /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/../j2sdk-image/bin/java -Djava.util.logging.config.file=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -XX:+EnableJFR -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/bootstrap.jar:/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/tomcat-juli.jar -Dcatalina.base=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Dcatalina.home=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Djava.io.tmpdir=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/temp org.apache.catalina.startup.Bootstrap start
98451 pts/22 S+ 0:00 grep --color=auto tomcat
$ dragonwell8_home/bin/jcmd 77522 JFR.start duration=10s filename=$PWD/rec3.jfr
77522:
Started recording 3\. The result will be written to:
/home/my/workdir/rec3.jfr
10 秒之后可以看到生成了 JFR 数据文件/home/my/workdir/rec3.jfr,使用 JMC 即可进行分析。
也可以不指定收集数据的时间,直接启动 JFR 收集,并且在需要的时候手动把所有生成的数据一次性 dump 到文件,
$ dragonwell8_home/bin/jcmd 2823 JFR.start filename=$PWD/rec4.jfr
2823:
Started recording 4\. No limit specified, using maxsize=250MB as default.
Use JFR.dump name=4 to copy recording data to file.
$ dragonwell8_home/bin/jcmd 2823 JFR.dump name=4 filename=rec4.jfr
2823:
Dumped recording "Recording-4", 332.7 kB written to:
/path/to/my/workdir/rec4.jfr
使用 JMC 分析性能
JFR 记录 Java 应用性能数据的输出是一个二进制的文件,我们借助于 JMC(Java Mission Control) 工具可以在图形化界面里面分析具体的性能数据。这个工具是开源产品,没有包括在 Alibaba Dragonwell 8里面,需要到 OpenJDK 的官方网站下载使用,https://jdk.java.net/jmc/。
请注意, Alibaba Dragonwell8 生成的JFR数据文件需要 7.0 或更高版本的 JMC 工具来分析。
打开 JMC 后,可以点击左侧的详细类别来详细分析采样时间段内发生的各种事件。
诊断调试支持
Alibaba Dragonwell 8 还内置一些方便的诊断特性,主要包括
大对象分配报警
可以通过新的 JVM 参数"-XX:ArrayAllocationWarningSize=",比如下面代码中分配了比较大的数组
public static void main(String[] args) {
doAlloc(32 * 1024 * 1024 + 1);
}
private static Object doAlloc(int size) {
return new byte[size];
}
执行时如果添加 ArrayAllocationWarningSize 选项就会打印出分配该数组时的 Java 堆栈
详细的 ParNew GC 日志支持
Alibaba Dragonwell 8 默认使用了 CMS (Concurrent Mark Sweep) 算法,新生代使用了 ParNew 算法,所以内置两个针对 ParNew GC 日志的增强
- 可以通过 jinfo 工具设置 PrintYoungGenHistoAfterParNewGC 选项来在下一次 Young GC 结束的时候打印新生代的对象类型直方图。命令如下
jinfo -flag +PrintYoungGenHistoAfterParNewGC <pid>
打印完成后,这个选项会被重置回 false 状态,防止过多的输出,一个典型的输出例子如下:
- 可以通过-XX:+PrintGCRootsTraceTime 来打印处理每一类 GC 根集所花费的详细 CPU 时间,输出示例如下:
精简版 HeapDump 支持
Alibaba Dragonwell 8 的 jmap 工具支持一个新的 dump 选项“mini”可以在做 heapdump 的时候跳过所有的原始类型数组的内容,从而大大减小生成的 heapdump 文件大小,这对于只需要排查类型、对象关系的场景会比较有帮助。
示例命令如下:
本文作者:中间件小哥
本文为云栖社区原创内容,未经允许不得转载。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。