Java诞生20年,拥有一大批优秀的企业级框架,践行OOP理念,更体现了长期运行条件下的严谨、稳定和高性能。相比之下,如今在需要快速迭代交付的云场景中,语言的简单性似乎是首要的要求,而传统的Java语言则显得有些过于沉重。今天,阿里JVM团队技术专家余磊(昵称:梁曦)分享了JVM团队如何面对和应对集团庞大的业务规模和复杂的业务场景。音乐没有国界,但音乐家有国界。云原生也是如此。虽然没有有限的编程语言,但是应用程序使用的编程语言决定了应用程序部署和运行的行为。ElasticHeapJava经常被诟病占用资源,其中最值得注意的就是Heap的内存占用。即使没有请求处理或对象分配,进程仍然会保留完整的堆内存空间,保证GC快速分配和操作内存。敏捷。AJDKZenGC/ElasticHeap双十一全面支持核心链路上百个应用和数十万个实例。JDK12开始支持定时触发并发mark的功能,remark时收缩Java堆返回内存。但是并没有解决stw增加暂停时间的问题,所以每次youngGC都不能归还内存。ElasticHeap在并发异步线程中完成了重复map/unmap和pagefault的内存处理开销,所以任何youngGC都可以及时归还内存或者及时恢复内存使用。ElasticHeap阿里巴巴实战ElasticHeap场景一:可预测的流量峰值ElasticHeap场景二:单机运行多个Java实例多个Java实例接受的流量任务比较随机,峰值不会重叠,可以有效降低整体负载空闲时间内存使用中的多个实例,提高部署密度。双十一验证核心交易系统采用ElasticHeap低功耗模式运行,大大降低了WSS(WorkingSetSize)规模实例。静态编译云端很多新的应用都选择了Go语言。很大的原因是Go应用程序不依赖于运行时。静态编译的程序启动速度快,不需要通过JIT预热。在阿里拥有大量Java代码的前提下,我们如何将这种能力注入到Java中呢?Java静态编译技术是一种激进的AOT技术,通过单独的编译阶段将Java程序编译成本地代码,运行时不需要传统的Java虚拟机和运行环境,只需要操作系统类库的支持。其工作原理如下图所示。静态编译技术实现了Java语言与原生程序的“融合”,将原始Java程序编译成具有Java行为的bootstrapped原生程序,从而兼具Java程序和原生程序的优点。JVM团队与SOFAStack团队紧密合作,率先在中间件应用中实现了静态编译。将应用程序的启动速度从60秒优化到3.8秒。双十一期间,静态编译应用稳定运行无故障。GC暂停时间为100毫秒,在业务允许范围内。内存使用和RT与传统Java应用程序相当。平坦的。综上所述,静态编译应用在稳定性、资源占用、RT响应等指标与传统Java应用基本一致的情况下,启动时间减少了2000%。Wisp2当你用最酷的Vert.X开发一个简单的web服务,准备体验最强的性能时,QA同学给你拿来一个1C2G的容器,让你压着,你却发现打不过no不管OthersGo应用程序是什么。经过研究发现,在这种核数较少的情况下,原来协程模型的性能要好很多。时代变了,Java过时了吗?AJDKWisp2回答了这个问题:Java也可以有高性能协程。今年是Wisp2大规模推出的第一年。Wisp2具有以下特点:整个Java运行时都支持协程调度,线程(如Socket.getInputStream().read())的阻塞将变得更轻协程切换。完全兼容ThreadAPI,在启用了Wisp2的JDK中,Thread.start()实际上创建了一个协程(轻量级线程),可以类比Go,只提供coroutine关键字go,没有暴露线程接口;我们也只是提供了一种创建协程的方法,应用程序可以透明地切换到协程。支持工作窃取,调度策略特别适合web场景,高压下调度开销极小。今年双十一期间,Wisp支撑了数百个应用和10万个容器,其中90%已经升级到Wisp2。我们可以看到在峰值附近,Wisp2机器的CPU降低了7%左右(Wisp1更低,Wisp2的朝向是RT,所以CPU会更高),这主要是轻量级节省下来的sysCPU调度。0点的CPU是相等的,也说明了一点:Wisp2解决了调度开销。当CPU低,调度没有压力的时候,没有区别。从RT来看,Wisp2机器的RT要低20%左右。RT明显降低的原因之一是这些机器的CPU压力非常大,协程的调度优势更容易体现出来。这样的优势可以帮助系统达到更高的水位,提高整体利用率,而不用担心RT过高导致系统雪崩。FDO双十一零点过后几分钟会有明显的CPU峰值。据资料分析,主要原因是双十一零点触发了JIT编译。比如程序中有这样的逻辑:if(is1111(LocalDate.now())){branch1}else{branch2}假设branch2在warm-up时一直在运行,那么JIT有理由相信后续基本上会转到branch2而不是Willcompilebranch1。在零点,我们进入branch1,此时我们需要触发反优化重编译方法。让我们看看AJDK是如何通过profiling来解决这个问题的。反优化原理及其危害JDK运行代码时,采用分层编译的方式动态编译Java方法。在最高级别(bestpeakperformance)的编译中,出于性能的考虑,会根据编译时收集到的信息做出一些乐观的假设。一旦不满足这些假设,就会发生去优化。比如热点方法中的某段代码只会在双十一执行,那么这段代码在预热过程中是不会被编译的,一旦双十一来临时执行这段代码就会触发执行的整个方法。去优化。去优化有两个负面影响。一是需要运行的方法从高效编译执行变为解释执行,运行速度降低100多倍。二是去优化的方式很快就会重新编译,编译线程很耗cpu。所以,去优化的危害会在双十一等流量短时间内暴增,与预热流量不一样的场景下尤为明显。ReducedeoptimizationthroughFDOFDO是feedbackdirectedoptimization的缩写,指的是参考之前JVM运行时的编译信息,指导本次运行时更好的编译。具体来说,我们采用两级方法来减少去优化。将每次运行时的去优化信息记录到一个文件中,下次运行时读取这个文件,在决定是否做乐观假设时参考文件中的信息,从而降低去优化的概率。信息显示,出现最多的去优化与if-else相关,占总数的一半以上。我们提供了一种方法,可以根据过去if-else去优化的信息关闭一条路径上所有相关的乐观假设。FDO对双十一的影响FDO是今年双十一上线的,目的是解决两个问题:1、双十一0:00流量高峰叠加导致的CPU使用率脉冲过高,反优化/编译高峰。2、预热效率低。经过压测前长时间的预热,当流量增加时,仍然伴随着大量的编译和去优化。第一个问题,我们收集了双十一高峰第一分钟的去优化/C2编译次数和CPU数据。可以看到,启用FDO后,高峰期的C2编译次数减少了约45%,反优化次数减少了约70%。CPU数据方面,在高峰期第一分钟开启FDO后,CPU从67.5左右下降到63.1,降幅约7.0%。第二个目标可以通过压测第一分钟的CPU数据来验证。开启FDO后,压测第一分钟CPU占用率从66.19下降到60.33%,降幅约10%。GraceZProfiler一直是全组排查Java应用各种问题的利器,而Grace作为其平台版本,从原来的单机版到现在的Master/Worker架构,进行了一系列的优化,以及引入任务排队机制,在高压下对用户任务进行排队,解决Worker不堪重负的问题。可维护性、扩展性和用户体验得到了质的提升,为后续工具平台的云化和开源打下了坚实的基础。目前已经集成了HeapDump功能,在继承ZProfiler功能的基础上做了一些优化,改进了解析引擎的版本,支持更全面的OQL语法。JDK11JDK8作为经典版本,正在大规模使用。虽然从JDK6、7迁移有一定的痛苦,但升级后的总体反馈是:“真香”。OpenJDK8的下一个稳定版本是OpenJDK11,JVM团队自然会积极跟进这个方向。目前,AJDK11支持AJDK8的Wisp2和多租户特性。本次双十一部分集群已经上线到JDK11,性能稳定。升级JDK11会给我们带来和升级JDK8一样的惊喜吗?我们可以在JDK11上体验最新的ZGC。ZGCJDK11引入了一个重要的特性:ZGC内存垃圾收集器。这个垃圾收集器声称能够在从几十GB到几TB的堆上将暂停时间保持在10毫秒以内。过去很多Java开发者都苦于垃圾收集器的暂停时间带来的延迟,而ZGC的短暂停特性无疑会成为未来Java开发者的新宠。目前ZGC在OpenJDK中还是一个实验性的特性,JDK11还没有在业界全面普及。JDK11只支持Linux上的ZGC(ZGCforMacOS和Windows预计在2020年3月发布的JDK14版本中支持),许多Java开发者还只能垂涎三尺,处于观望状态。向来敢吃螃蟹的我们怎能气馁?阿里JVM团队和数据库团队已经开始在ZGC上运行数据库应用,并根据运行效果对ZGC进行了相应的改进,包括ZGC的页面缓存机制优化、ZGC的Trigger时序优化等。从9月份开始,两个团队就推动在线数据库应用在ZGC上运行。稳定运行两个月,顺利通过双十一考试。网上反馈的效果喜人:1、JVM停顿时间保持在官方10ms以内;2、ZGC对在线运行集群的平均RT和毛刺指标有较大提升。小结从以上功能特点可以看出,AJDK已经从传统的ManagedRuntime脱胎换骨。未来,AJDK将继续致力于提升云端应用的开发体验,通过底层创新为上层应用提供更多可能。
