Skip to content

阿里巴巴Dragonwell8用户指南

唐佳未 edited this page Jan 12, 2024 · 25 revisions

简介

阿里巴巴有着最丰富的Java应用场景,覆盖电商,金融,物流等众多领域,世界上最大的Java用户之一。 作为OpenJDK的下游, Alibaba Dragonwell是阿里巴巴内部OpenJDK定制版AJDK的开源版本, AJDK为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,100,000+ 服务器的阿里巴巴数据中心。Alibaba Dragonwell是OpenJDK的下游(friendly fork),使用了和OpenJDK一样的licensing。阿里会更紧密地和OpenJDK等开源社区协作, 贡献更多的patches, 促进Java技术的持续发展。

使用Alibaba Dragonwell

您可以通过如下步骤使用Alibaba Dragonwell

安装Alibaba Dragonwell

  • 选项 2, 使用YUM工具安装 Alibaba Dragonwell 是被Alibaba Cloud Linux 2 (Aliyun Linux2) YUM仓库官方支持维护的软件包,这个仓库同时和Aliyun Linux 17.1, RedHat Enterprise Linux 7和CentOS 7保持兼容。 使用的Alibaba Cloud Linux 2用户可以简单的通过如下命令安装Alibaba Dragonwell软件: sudo yum install -y java-1.8.0-alibaba-dragonwell 对于其他使用前面提到的和Alibaba Cloud Linux2兼容的Linux发行版的用户,需要添加一个新的YUM配置到/etc/yum.repos.d目录 (例如: /etc/yum.repos.d/alinux-plus.repo) ,
# plus packages provided by Aliyun Linux dev team
[plus]
name=AliYun-2.1903 - Plus - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/alinux/2.1903/plus/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/alinux/RPM-GPG-KEY-ALIYUN

然后您就可以使用相同的命令来安装Alibaba Dragonwell软件了 sudo yum install -y java-1.8.0-alibaba-dragonwell.

为您的Java应用启用Alibaba Dragonwell

将您的应用脚本或者环境变量中的JDK目录变量(一般是JAVA_HOME)指向上一步中安装的Alibaba Dragonwell目录。 重启应用以使用Alibaba Dragonwell JDK配置

JWarmup

JWarmup的基本原理: 根据前一次程序运行的情况,记录下热点方法、类编译顺序等信息,在应用下一次启动的时候积极加载相关的类,并积极编译相关的方法,进而应用启动后可以直接运行编译好的Java代码(C2编译)。

上图显示了JWarmup典型的用法:

  • 在Beta灰度环境,进行应用压测,记录下热点方法、类编译顺序等信息。
  • 在Production环境,使用提前记录的profiling data提前编译热点方法。

使用步骤

  1. 记录阶段(一般是beta环境), 使用CMS的示例,在5分钟后生成profiling data:
java -XX:-ClassUnloading -XX:-CMSClassUnloadingEnabled -XX:-ClassUnloadingWithConcurrentMark -XX:CompilationWarmUpLogfile=jwarmup.log -XX:+CompilationWarmUpRecording -XX:CompilationWarmUpRecordTime=300

注: 目前记录模式暂不支持类卸载。

  1. 使用阶段(一般是生产环境)
-XX:+CompilationWarmUp -XX:-TieredCompilation -XX:CompilationWarmUpLogfile=jwarmup.log -XX:CompilationWarmUpDeoptTime=0

注: 目前编译模式暂不支持分层编译。

注: JWarmUp会在指定时间退优化warmup编译的方法,设置CompilationWarmUpDeoptTime为0可以取消这个定时。

  1. 根据你的代码逻辑“个性化”JVM的编译时机 在你的应用启动完成后(完成了非常必要的初始化),通过以下API通知JVM你的启动完成:
com.alibaba.jwarmup.JWarmUp.notifyApplicationStartUpIsDone()

Java Flight Recorder

注意:EnableJFR 在 8.4.4 中被标记为废弃,在 8.5.5 中被移除,请在使用 8.5.5 或更高版本时不要使用此参数。

Java Flight Recorder (JFR) 是一款用于收集Java应用运行过程中的诊断及性能数据的工具,目前已经被集成进Alibaba Dragonwell中。在使用默认配置的情况下,JFR带来的额外开销将小于2%,因此可以用在生产环境。

Alibaba Dragonwell在8.12.16版本之前(包括8.12.16)通过参数EnableJFR来控制是否打开JFR功能,默认关闭;在8.12.16之后的版本移除了此选项。你可以使用 ‘-XX:+EnableJFR’ 开启JFR,如:

java -XX:+EnableJFR -XX:StartFlightRecording=duration=1m,filename=rec.jfr MyApp

在使用JTreg跑测试用例时,需要增加一个参数 -vmoption:-XX:+EnableJFR,否则jfr相关的测试会失败。如:

make test JTREG_TEST_EXTRA_OPTIONS=-vmoption:-XX:+EnableJFR TEST=jdk_jfr

JMC可以用于分析JFR产生的事件记录,需要使用7.0及以上版本。 目前JMC还不能用来控制Alibaba Dragonwell的JFR, 在下一个版本我们会修复这个问题。 关于JFR及JMC更详细的信息,可以参考Oracle的文档。

G1ElasticHeap

G1ElasticHeap支持在G1中动态归还堆物理内存并降低Java进程的内存占用。使用此功能需要添加选项:-XX:+UseG1GC -XX:+G1ElasticHeap.

使用方式

G1ElasticHeap有三种不同的模式开启内存归还和调整

1. 周期GC释放内存

需要开启选项-XX:+ElasticHeapPeriodicUncommit或者使用jinfo动态开启:

jinfo -flag +ElasticHeapPeriodicUncommit PID

相关参数:

ElasticHeapPeriodicYGCIntervalMillis, 15000
(期望目标young gc间隔,默认15秒)
(例如当Java运行在MaxNewSize=4g, young GC每30秒一次, G1ElasticHeap可以保持15秒一次的GC,并把young区控制在2G,节约2G的内存)

ElasticHeapPeriodicInitialMarkIntervalMillis, 3600000
(期望的目标并发GC的间隔, 默认一小时。未被使用的老区内存会在最后一次mixed GC后释放)

ElasticHeapPeriodicUncommitStartupDelay, 300
(Java启动延迟一段时间后开启归还, 默认300秒)

ElasticHeapPeriodicMinYoungCommitPercent, 50
(保持至少MaxNewSize的比例,默认最少保留50%)

2. 分代限制模式

分别限制新区和老区的大小。使用jcmd或者MXBean开启。 用例:

jcmd PID GC.elastic_heap young_commit_percent=40 uncommit_ihop=40

young_commit_percent: 保持新区(MaxNewSize)的比例,剩余的部分将被归还

uncommit_ihop: 触发老区concurrent mark/mixed GC的阈值(类似InitiatingHeapOccupancyPercent)。老区不使用的内存将在最后一次mixed GC后被归还。

使用MXBean:

MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 
ElasticHeapMXBean elasticHeapMXBean = ManagementFactory.newPlatformMXBeanProxy(server,
                                                     "com.alibaba.management:type=ElasticHeap", 
                                                      ElasticHeapMXBean.class); 
elasticHeapMXBean.setYoungGenCommitPercent(40);
elasticHeapMXBean.setUncommitIHOP(40);

3. Softmx模式

动态限制整堆为Xmx的一个百分比。

使用jcmd:

jcmd PID GC.elastic_heap softmx_percent=60

使用MXBean:

elasticHeapMXBean.setSoftmxPercent(70);

其他G1ElasticHeap 高阶选项:

ElasticHeapMinYoungCommitPercent, 10
(保留新区MaxNewSize的最小比例)

ElasticHeapYGCIntervalMinMillis, 5000
(保持young gc的最小间隔)

ElasticHeapInitialMarkIntervalMinMillis, 60000
(保持concurrent mark周期的最小间隔)

ElasticHeapEagerMixedGCIntervalMillis, 15000
(保障mixed GC的间隔,使得当需要在最后一次mixed gc归还时,保障mixed GC能及时发生)

ElasticHeapOldGenReservePercent, 5
(当在最后一次mixed GC归还老区内存时,保留不归还的最小比例)

ElasticHeapPeriodicYGCIntervalCeilingPercent, 25
ElasticHeapPeriodicYGCIntervalFloorPercent, 25
(保持实际young GC的间隔在这2个值之间
ElasticHeapPeriodicYGCIntervalMillis * (100 - ElasticHeapPeriodicYGCIntervalFloorPercent) / 100 and
ElasticHeapPeriodicYGCIntervalMillis * (100 + ElasticHeapPeriodicYGCIntervalCeilingPercent) / 100 )

Serviceability

  1. 迷你Heapdump支持 Alibaba Dragonwell允许您在使用 jmap 工具生成heapdump的时候忽略掉所有原始类型数组的内容,只dump出对象引用等信息,从而缩小生成的Heapdump文件大小。使用时,只需要给-dump子命令添加mini参数即可。

示例命令:

jmap -dump:live,mini,format=b,file=heap121.bin  <PID>
  1. -XX:+PrintYoungGenHistoAfterParNewGC 这个参数会打印在一次ParNew GC之后的young区对象的histogram。该参数可以通过jinfo动态打开:
jinfo -flag +PrintYoungGenHistoAfterParNewGC <pid>
  1. -XX:+PrintGCRootsTraceTime 这个参数会打印一次ParNewGC的具体耗时,类似于G1的gclog显示,这个参数主要用于用户排查时间较长的gc暂停时间。该参数可以通过jinfo动态打开关闭:
jinfo -flag +PrintGCRootsTraceTime <pid> // turn on the flag
jinfo -flag -PrintGCRootsTraceTime <pid> // turn off the flag

修正GC日志中metaspace数值显示错误 原先GC日志中在每次Full GC之后显示metaspace数值可能是错误的,Alibaba Dragonwell修复了这个问题

  1. -XX:ArrayAllocationWarningSize=<Size> 增加参数-XX:ArrayAllocationWarningSize=xxx,默认值512M。当分配大对象的大小超过该值的时候,标准输出中打印大数组的分配堆栈,用来排查大数组分配导致的GC问题。

该参数可以通过jinfo动态修改:

// print the allocation stack if more than 1M array allocated
jinfo -flag ArrayAllocationWarningSize=1048576 <pid>
// back to default value(512M)
jinfo -flag ArrayAllocationWarningSize=536870912 <pid>

JFR Object Profiling

Download JMC tools: Linux, OSX, Windows

在启动阶段通过参数 -XX:FlightRecorderOptions=sampleobjectallocations=true 开启这个特性。目前该特性使用当前线程在 C2 中创建的新对象数量作为采样的依据,用户可以通过 -XX:FlightRecorderOptions=sampleobjectallocations=true,objectallocationssamplinginterval=512 设置采样的间隔,默认值为1024。 然后可以通过以下步骤生成记录文件:

  1. 通过 jcmd <pid> JFR.start settings=ObjectProfiling.jfc name=MyRecording 开启一次 JFR 记录, ObjectProfiling.jfc 需要包含以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0">
  <event name="com.oracle.jdk.OptoInstanceObjectAllocation">
    <setting name="enabled">true</setting>
  </event>
  <event name="com.oracle.jdk.OptoArrayObjectAllocation">
    <setting name="enabled">true</setting>
  </event>
</configuration>
  1. 运行一定时间后, 通过 jcmd <pid> JFR.stop name=MyRecording filename=dump.jfr 生成记录文件。

得到记录文件之后,用户可以通过 JMC 进行对象创建的热点路径分析。为了更直观的分析,我们提供了经过简单增强后的 JMC 构建,效果图如下:

增加 Object Profiling Tab,可以清楚的分析出热点对象:

增强 Flame 功能,可以按照对象分配大小计算权重:

如何打开火焰图:
1.窗口 | 显示视图 | 其他...
2.选择 Mission Control / Flame View

Metaspace Dump

Metaspace Dump 功能可以获取 Metaspace 区域的快照,支持文本格式的输出,主要用于排查 Metaspace 区域引发的 FGC 问题。和开发者熟悉的 Heap Dump 功能有些类似。

使用方式(请先确保目标进程使用的是 Alibaba Dragonwell JDK):

$ jcmd <pid> Metaspace.dump filename=<direcotry or file name>

此外还提供了以下 JVM 选项:

  • -XX:+MetaspaceDumpBeforeFullGC:在 Full GC 前触发一次 Dump,默认关闭。
  • -XX:+MetaspaceDumpAfterFullGC:在 Full GC 后触发一次 Dump,默认关闭。
  • -XX+MetaspaceDumpOnOutOfMemoryError:在 Metaspace 引发 OOM 时触发一次 Dump,默认关闭。
  • -XX:MetaspaceDumpPath=<path>:指定 Metaspace Dump 输出的路径(文件名或目录),默认为进程工作目录下的 java_pid.mprof。

以上选项可通过 jinfo 动态修改。

Wisp

Wisp在JVM上提供了一种用户态的线程实现。开启Wisp2后,Java线程不再简单地映射到内核级线程,而是对应到一个协程,JVM在少量内核线上调度大量协程执行,以减少内核的调度开销,提供web服务器的性能。更多技术细节详见Wisp文档。

用法

只需添加JVM参数即可开启Wisp2。引入一个简单测试:

public class Sleep {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { /* pass */}
            }).start();
        }
    }
}
$ javac Sleep.java
$ java -XX:+UnlockExperimentalVMOptions -XX:+UseWisp2 Sleep &
$ jstack $! | grep -A1 -- '- Coroutine' 

随后可以通过jstack观察到起来的线程都以协程的方式在运行了。

 - Coroutine [0x7f6d6d60fb20] "Thread-978" #1076 active=1 steal=0 steal_fail=0 preempt=0 park=0/-1
        at java.dyn.CoroutineSupport.unsafeSymmetricYieldTo(CoroutineSupport.java:138)
--
 - Coroutine [0x7f6d6d60f880] "Thread-912" #1009 active=1 steal=0 steal_fail=0 preempt=0 park=0/-1
        at java.dyn.CoroutineSupport.unsafeSymmetricYieldTo(CoroutineSupport.java:138)

...

可以看到最上方的frame上的方法是协程切换,因为线程调用了sleep,yield出了执行权。

更多Wisp的设计实现与API相关信息,请参考Wisp文档

获取支持

如果您需要获取关于Alibaba Dragonwell的支持信息,请首先查看Alibaba Dragonwell FAQ文档。 或者您也可以发送电子邮件到 dragonwell_use@googlegroups.com 寻找帮助。 您也可以使用钉钉输入下面群号加入群聊获取支持

钉钉群群号 35434688

Clone this wiki locally