当前位置: 首页 > 科技观察

JDK在大数据平台上的探索与研究

时间:2023-03-15 01:45:47 科技观察

本文旨在概述京东在JDK方向的尝试与探索,以及JDK项目的背景、基本特点和未来的工作方向。JDK特性的技术探讨、实现细节和效果将在后续系列文章中深入探讨。一、HDFS简介HDFS作为底层的分布式存储服务而存在,是Hadoop的一个分布式文件系统组件。HDFS具有高度容错性,旨在部署在低成本硬件上。HDFS提供对应用程序数据的高吞吐量访问,适用于具有大规模数据集的应用程序。HDFS采用基于Master/Slave主从架构的分布式文件系统。一个HDFS集群包括一个Master节点(NameNode)和多个Slave节点(DataNode)服务器。文件以块的形式存储在DataNode节点中。NameNode主要负责响应客户端请求,打开和关闭文件,重命名文件和目录,确定块到具体Datanode节点的映射。Datanode在Namenode的命令下创建、删除和复制块。2、JVM对HDFS的作用由于HDFS是用Java开发的,运行在JVM上,如何从JVM的角度提升HDFS的能力是主要的研究方向之一。从JVM的角度来看,NameNode的特点是进程生命周期长,对象创建频繁,资源利用率高,对内存资源要求高。NameNode的性能是HDFS性能的关键。DataNode节点的特点是进程生命周期短,大部分进程在创建后对文件块进行操作后退出。如何优化JVM,使其更适合HDFSNameNode和DataNode的工作特点,是JDK研发的主要方向。三、京东在通用JDK上的尝试1、使用OracleJDK1.8的心得在京东,我们尝试使用Oracle的JDK1.8作为HDFS的JDK方案。经过不断的努力和调参,HDFS已经在OracleJDK1.8环境下稳定运行。但是,随着京东业务的不断增长,对HDFS的要求也越来越高,OracleJDK1.8无法在以下问题上提供更多的帮助:性能优化:虽然OracleJDK1.8的JVM有很多高级的优化功能,比如分层编译器,高效的CMS垃圾收集器等,但主要针对通用Java程序的性能进行了优化,缺乏针对分布式工作环境的具体优化。由于oracleJDK1.8的源码无法修改,调整参数不能从根本上解决问题。不可控的GC:虽然OracleJDK1.8提供的优秀的CMS垃圾收集器可以有效改善GC停顿时间带来的性能损失,但是在实际使用中发现GC停顿时间还是达不到要求,比如YoungGC的时间是仍然大约1秒,而OldGC消耗大约60秒。如果发生FullGC,往往会导致NameNode停顿时间过长,导致系统卡顿。结果往往是灾难性的。内存利用率低:对于NameNode节点,可以使用的物理内存为512GB。为了避免JVM中老年代GC和FullGC时间过长造成的灾难性后果,NameNode节点只能将Java堆配置为200GB左右。通常在NameNode节点的机器上只运行NameNode进程和一个轻量级的ZKFC进程,物理内存无法得到有效利用。另一方面,NameNode的承载能力受限于Java堆的大小,这就限制了HDFS的整体承载能力。JDK版本更新:由于以上问题不断出现,2019年JDK1.8将停止更新,同时需要尝试新的JDK,OpenJDK是否可以帮助解决问题。2、试用openJDK11随着openJDK的不断进化,为了缓解上述问题,也试用了OpenJDK11,对比openJDK1.8,发现openJDK11可能在以下几个方面有优势:G1GC:openJDK11默认使用G1GC算法,与CMS相比,G1有以下优势:内存碎片更小:由于CMS老年代采用Mark-sweep算法,不会在每次OldGC时都进行compacting,所以CMS老年代空间经常会引入碎片问题。但是G1采用块复制算法,内存碎片问题只存在于G1的块中。与CMS相比,它的内存利用率更高,FullGC和OOM的可能性更低。可控的GC停顿时间:G1算法的一个典型特点是允许用户提供预期的GC停顿时间,内部使用统计预测的方法有效控制下一次即将到来的GC算法的停顿时间,从而优化GC的性能损失.更丰富的性能分析工具:OpenJDK11引入了JavaFrameRecorder(JFS),这是原来的oracleJDK1.8商业版才有的特性。JFR可以对Java程序进行性能无损或轻微损失的采样,有助于分析性能、功能瓶颈,指导优化。HDFS更高的负载能力:由于OpenJDK11使用G1作为默认的GC算法,因此可以更高效地使用堆内存。同时由于G1算法的设计和优化,FullGC的概率很低,FullGC的停顿时间也得到了。经过优化,因此相对于OracleJDK1.8的CMS,对于HDFSNameNode,其负载能力受堆大小的限制更为宽松。虽然OpenJDK11可以帮助缓解一系列问题,但是对于京东大数据来说,仅仅使用原生的OpenJDK11仍然缺乏针对性的优化。目前主要存在以下问题:针对大堆的优化:由于openJDK上G1的一些内部限制,其针对大堆的性能,比如360GB堆,还没有达到理想状态。大堆的工具开发:以JMap为例。当堆内存较大时,一次JMap操作方便了整个堆内存,耗时较多。我们经常会遇到JMap导致假死的情况。HDFS的定制工作:另外,我们还是希望JDK有一些可用的特性,帮助我们增强HDFS在问题分析、危机管理、在线分析等方面的能力。4.京东定制JDK经过以上尝试,结合HDFS业务特点和优化需求。最后决定在OpenJDK11的基础上有针??对性地开发和优化openjdk,打造京东定制化JDK。一、JDJDK特性介绍JDJDK目前??除了具备openJDK11的特性外,还具有以下能力:(1)JDK8兼容支持javah:由于JDK8具有Javah工具,可以根据Java类定义生成相应的JNI实现文件。C/C++头文件。在大型项目中,比如Hadoop,Yarn会使用Javah来生成JNI头文件。从JDK10开始,JDK中去掉了javah工具,取而代之的是javac-h功能,但是由于javac-h在使用上和javah不同,而且在复杂的项目中,如果想用javac-h代替javah,必须修改编译系统,工作量和难度都比较大。为了在京东内部顺利升级JDK,重写了javah,使其可以成功使用javac–h生成JNI头文件。(2)扩大G1区域大小:由于openJDK的限制,G1GC的区域大小只能达到32MB,JVM内部推荐的区域数量为2048,即最适合G1GC的堆大小为64GB(2048*32MB),而业务量要求NameNode堆至少180GB,所以JDJDK确定了优化G1GC对大堆支持的目标,以提高管理节点的性能。经过调研,G1GC的region调整其实有两个方向。一是保持region大小不变,增加region数量以适应大堆。例如,对于一个180GB的堆,region大小保持不变为32MB,那么你需要创建5760个region。这种方案的好处是保持region的大小不变,可以将分配的影响降到最低,但是同时,由于G1算法需要同步各个region之间的引用关系,如果heap的数量太多大了,同步的开销会增加,从而影响GC的效率。另一种解决方案是增加区域的大小,以将区域数量保持在2048个或小幅增加。特点是增加region可能会导致应用对象分配行为发生变化,但对region间引用关系的同步影响相对较小。为了达到优化性能的目的,对NameNode进行如下分析:通过收集GCdebug的日志信息可以看出,NameNode的对象分配率非常频繁,旧的空间分配率达到1MB/s,即大量objects被频繁增加老年代有大量的TLABrefiles,TLABfill的频率大约是每分钟30000次。TLABfill表示分配进入慢速路径,需要在非TLAB中替换或分配TLAB。因此,对象分配性能是NameNode性能的关键点之一。结合上面的分析,优化了JDK的regionsize的上限,针对regionsize相应的修改了G1。以下是优化实验得到的数据。可以看出,TLAB的填充次数从每分钟3万次下降到2万次,即对象分配到慢路径的概率下降了33%。(3)多线程锁性能优化:JDJDK版本升级后,运维研发人员发现在大数据平台运行过程中,G1运行时会出现2s左右的超长YoungGC,而同样规模的YGC大多只有200ms左右。如下图绿线所示。经过分析,G1中2sGC的主要原因是偏向锁函数的撤销过于频繁。使用JFR,可以看到以下现象。综合以上分析,管理节点采用-XX:-UseBiasedLocking后,2sGC消失,如上图蓝线所示。(4)Java堆的动态扩展:Java程序启动时,需要程序员为JVM预先设置堆内存的上限,即指定-Xmx的大小(或者使用JVM默认参数).但是在实际使用中,很难明确计算出应该使用的Java堆的上限,尤其是在线系统中的管理进程,极有可能出现OOM(Out-Of-Memory)异常导致管理进程退出,造成灾难性的后果。另一方面,考虑到对系统资源的占用,Java程序往往要求JVM不要占用大量的系统内存,即使-Xmx的值小于RAM的大小,所以在程序运行时,Java进程经常会因为OOM而退出,而系统RAM却还剩下很多。为了缓解OOM问题,JDK在G1GC的基础上开发了动态扩展堆大小的功能。该函数在JVM堆内存使用正常的情况下,保持java堆在-Xmx以下,当JVM发现当前进程的Java堆被大量占用时,会发出告警,以便运维人员监控系统根据当前业务情况。根据RAM使用情况,动态开启Java堆扩展功能,JVM按一定比例扩展Java堆,保证JVM顺利度过业务繁忙期。当业务量减少,堆使用率低于一定阈值时,JVM会使用G1GC回收扩大的堆区域,以保证JVM进程在正常情况下不会对系统内存造成额外的压力。(5)定时定时触发GC:经排查发现,京东的业务呈现明显的时间周期性,比如某个集群在某个时间段内基本处于空闲状态。在忙碌状态下,堆内存和CPU资源都集中在业务处理上。如果此时发生OldGC或FullGC,或者YoungGC发生过于频繁,系统的业务处理能力就会下降。为了降低GC对业务处理能力的影响,京东JDK在G1GC的基础上开发了周期GC功能。运维人员可以在每天系统不繁忙的时间段定时触发多次YoungGC和必要的MixedGC/FullGC来清理Java堆中的垃圾,从而减少高峰期GC触发的频率和时间。(6)JVM及时将未使用的内存(UncommittedMemory)返回给系统:JDK12特性,JDK目前??支持。这个功能主要是为了节省物理内存空间。JDK11版本的G1不会及时将空region返回给OS,只会在FullGC或OldGC并发阶段将回收的region返回给OS。但是,由于G1的设计目标是避免FullGC,尽量少触发OldGC,所以在实际运行过程中,G1堆占用的物理内存不会长时间释放给系统,导致G1堆占用的内存JVM进程比实际使用率要高很多。在多进程、多任务环境下,系统内存资源不能作为一个整体进行有效分配和使用,同时增加了对内存硬件的需求,增加了企业的成本。京东JDK在JDK11的基础上引入了JDK12的JEP346特性——“及时回收系统未使用的UncommittedMemory”。这个特性在JVM内部引入了一个监控机制。JVM会自动触发concurrentGC或者FullGC将未提交的region回收给系统。(7)ReversibleG1MixedGC保证GC停顿时间:JDK12特性,有效减少和控制G1停顿时间。G1GC的主要设计目标是保证G1的停顿时间在可控范围内。用户可以通过-XX:MaxGCPauseMills参数指定G1暂停时间的上限。G1GC会尽量保证每次GC的时间不会超过-XX:MaxGCPauseMills。在JVM内部,G1GC会在Concurrent阶段根据暂停时间的上限选择需要回收的集合(CollectSet),然后在暂停阶段回收这些集合中的对象。在JDK11版本中,CollectionSet一旦确定就无法更改。但是由于CollectionSet是JVM根据历史GC信息推断出来的,如果推断与真实情况误差过大,MixGC(oldGC)的挂起时间就会过长。远远超过-XX:MaxGCPauseMills设定的目标。京东JDK从JDK12引入了JEP344特性——AbortableMixedCollectionsforG1。该特性可以将CollectionSet分解为“必须回收”和“可选回收”两部分。当MixedGC发生时,GC回收后“必须回收”部分后,会根据目标停顿时间的剩余量从“可选回收”部分中循环选择回收集合,从而保证整体停顿时间GC的是可控的。(8)默认类型信息共享文件(ClassDataSharing-CDSArchive):ClassDataSharing(CDS)有助于加快Java程序的启动时间,同时允许多个JVM实例重用SharedArchive以减少内存脚印。JDK10进一步完善了CDSExpansion,将应用数据保存在SharedArchive中:ApplicationClass-datasharing(seeJEP310)对于CDS,在JEP中的介绍如下:Wecansaveabout340MBofRAMforaJavaEEappserverthatincluded6JVM进程总共消耗13GBRAM(其中约2GB用于类元数据)。我们可以将JEdit基准测试的启动时间缩短20-30%。我们可以在4个JVM进程中将嵌入式Felix基准测试的RAM使用量减少18%。JD.comJDK在JDK12中引入了一个新的CDS特性——默认CDS存档。该函数在编译阶段生成一个默认的Archive,用户无需指定JVM选项-Xshare:auto即可享受CDS的优势。(9)并行高效的JMapJava堆分析工具:作为Java开发者常用的工具,JMap一般可以在调查OOM、查看堆对象分布等方面发挥重要作用。但是在日常工作中发现,对于大堆,比如堆内存配置为-Xmx200g时,在线系统上运行JMaphisto的时间非常长,影响了响应速度整个JVM进程。一旦JVM进程被KILL,JMaphisto也将运行。无法提供有效信息。经查,JMap工具扫描Java堆时是单线程作业,只有整个堆扫描完成后才会输出统计信息。针对JMap的问题,京东JDK团队对JMap进行了扩展,实现了其并行增量扫描方案。将JMaphisto在大堆上的扫描并行化,计算运行时的中间结果。将JMap在200GB堆扫描中的性能提高了2倍,并使JMap在运行过程中不断输出中间结果,这样即使JVM进程退出,JMap仍能为分析内存使用情况提供有效信息。2、京东JDK优化效果经过一系列工作,京东JDK已成功应用于京东大数据平台HDFS的NameNode节点,其对管理节点的优化达到了50%。节点文件承载量从4亿增加到10亿,承载量提升1.5倍。缓解业务方需求,节省人力。G1GC也进行了优化。优化后的G1GC与之前JDK8CMS的YoungGC停顿时间对比如下图:GC发生次数如下:在加/解锁和线程同步方面,京东JDK团队也进行了in-深入研究。优化,除了上面提到的偏向锁,还有使用JVMinstrumentation等工具在线优化锁相关的字节码。针对不同的HDFS访问,优化效果如下:Mkdir:Delete:Getfileinfo:Rename:5.京东JDK的发展方向未来,京东JDK团队将更加专注于降本增效。我们计划做更多的尝试和创新,例如:针对特定使用场景的独立堆区半自动GC针对大数据应用场景的基于GC算法的开发和优化基于Graal的AOT功能的开发和优化同时之后,京东JDK团队也将积极参与openJDK社区的开发和研究,将JDKJDK的特性尽可能的贡献给社区,让更多的人使用。作者简介:臧林,京东JVM专家,主要负责京东JDK针对京东大数据业务的定制开发和优化。专注于JVM中的内存管理、runtime运行时以及JIT编译器的性能分析和优化。【本文来自专栏作者张凯涛微信公众号(凯涛博客)公众号id:kaitao-1234567】点此查看作者更多好文