《Java应用提速(速度与激情)》——四、JDK提速

简介: 《Java应用提速(速度与激情)》——四、JDK提速

1. AppCDS

 

1) 现状

 

CDSClass Data Sharing在Oracle JDK1.5被首次引入,在Oracle JDK8u40中引入了AppCDS,支持JDK以外的类但是作为商业特性提供。随后Oracle将AppCDS贡献给了社区,在JDK10中CDS逐渐完善,也支持了用户自定义类加载器又称AppCDS v2

 

目前CDS在阿里的落地情况:

 

热点应用A使用CDS减少了10秒启动时间

云产品SAE和FC在使用Dragonwell11时开启CDS、AOT等特性加速启动

 

经过十年的发展,CDS已经发展为一项成熟的技术。但是很容易令人不解的是CDS不管在阿里的业务还是业界即便是AWS Lambda都没能被大规模使用。关键原因有两个

 

a) AppCDS在实践中效果不明显

 

jsa中存储的InstanceKlass是对class文件解析的产物。对于boot classloader加载jre/lib/rt.jar下面的类的类加载器和systemapp类加载器加载-classpath下面的类的类加载器,CDS有内部机制可以跳过对class文件的读取,仅仅通过类名在jsa文件中匹配对应的数据结构。

 

Java语言还提供用户自定义类加载器custom class loader的机制,用户通过Override自己的Classloader.loadClass()查找类,AppCDS在为customer class loade时加载类是需要经过如下步骤

 

调用用户定义的Classloader.loadClass(),拿到class byte stream

计算class byte stream的checksum,与jsa中的同类名结构的checksum比较

如果匹配成功则返回jsa中的InstanceKlass,否则继续使用slow path解析class文件

 

b) 工程实践不友好

 

使用AppCDS需要如下步骤

 

针对当前版本在生产环境启动应用,收集profiling信息

基于profiling信息生成jsajava sahred archivedump

将jsa文件和应用本身打包在一起,发布到生产环境

 

由于这种trace-replay模式的复杂性,在SAE和FC云产品的落地都是通过发布流程的定制以及开发复杂的命令行工具来解决的。

 

2) 解决方案

 

针对上述的问题1,在热点应用A上CDS配合JarIndex或者使用编译器团队开发的EagerAppCDS特性原理见5.1.3.1都能让CDS发挥最佳效果。

 

经验证,在热点应用A已经使用JarIndex做优化的前提下进一步使用EagerAppCDS依然可以获得15秒左右的启动加速效果。

 

3) 原理

 

面向对象语言将对象数据和方法对象上的操作绑定到了一起,来提供更强的封装性和多态。这些特性都依赖对象头中的类型信息来实现,Java、Python语言都是如此。Java对象在内存中的layout如下:

+-------------+
|  mark       |
+-------------+
|  Klass*     |
+-------------+
|  fields     |
|             |
+-------------+

 

mark表示了对象的状态,包括是否被加锁、GC年龄等等。而Klass*指向了描述对象类型的数据结构InstanceKlass

/  InstanceKlass layout:
//    [C++ vtbl pointer           ] Klass
//    [java mirror                ] Klass
//    [super                      ] Klass
//    [access_flags               ] Klass
//    [name                       ] Klass
//    [methods                    ]
//    [fields                     ]
...

 

基于这个结构,诸如o instanceof String这样的表达式就可以有足够的信息判断了。要注意的是InstanceKlass结构比较复杂,包含了类的所有方法、field等等,方法又包含了字节码等信息。这个数据结构是通过运行时解析class文件获得的,为了保证安全性,解析class时还需要校验字节码的合法性非通过javac产生的方法字节码很容易引起jvm crash

 

CDS可以将这个解析、校验产生的数据结构存储dump到文件,在下一次运行时重复使用。这个dump产物叫做Shared Archive,以jsa后缀java shared archive

 

为了减少CDS读取jsa dump的开销,避免将数据反序列化到InstanceKlass的开销,jsa文件中的存储layout和InstanceKlass对象完全一样,这样在使用jsa数据时,只需要将jsa文件映射到内存,并且让对象头中的类型指针指向这块内存地址即可,十分高效。

Object:
+-------------+
|  mark       |         +-------------------------+
+-------------+         |classes.jsa file         |
|  Klass*     +--------->java_mirror|super|methods|
+-------------+         |java_mirror|super|methods|
|  fields     |         |java_mirror|super|methods|
|             |         +-------------------------+
+-------------+

 

a) Alibaba Dragonwell对AppCDS的优化

 

上述AppCDS for custom classloader的加载流程更加复杂的原因是JVM通过classloaderclassName二元组来唯一确定一个类。

 

对于BootClassloader、AppClassloader在每次运行都是唯一的,因此可以在多次运行之间确定唯一的身份

对于customClassloader除了类型,并没有明显的唯一标识。AppCDS因此无法在加载类阶段通过classloader对象和类型去shared archive定位到需要的InstanceKlass条目。

 

Dragonwell提供的解决方法是让用户为customClassloader标识唯一的identifier加载相同类的classloader在多次运行间保持唯一的identifier。并且扩展了shared archive,记录用户定义的classloader identifier字段,这样AppCDS便可以在运行时通过identifierclassName二元组来迅速定位到shared archive中的类条目。从而让custom classloader下的类加载能和buildin class一样快。

 

在常见的微服务workload下,我们可以看到Dragonwell优化后的AppCDS将基础的AppCDS的加速效果从10%提升到了40%。

 

2. 启动profiling工具

 

1) 现状

 

目前有很多Java性能剖析工具,但专门用于Java启动过程分析的还没有。不过有些现有的工具,可以间接用于启动过程分析,由于不是专门的工具,每个都存在这样那样的不足。

 

比如async-profiler,其强项是适合诊断CPU热点、墙钟热点、内存分配热点、JVM内锁争抢等场景,展现形式是火焰图。可以在应用刚刚启动后,马上开启aync-profiler,持续剖析直到应用启动完成。async-profiler的CPU热点和墙钟热点能力对于分析启动过程有很大帮助,可以找到占用CPU较多的方法,进而指导启动加速的优化。

 

async-profiler有2个主要缺点

 

第1个是展现形式较单一,关联分析能力较弱,比如无法选择特定时间区间,也无法支持选中多线程场景下的火焰图聚合等。

第2个是采集的数据种类较少,看不到类加载、GC、文件IO、SocketIO、编译、VM Operation等方面的数据,没法做精细的分析。

 

再比如arthas,arthas的火焰图底层也是利用async-profiler,所以async-profiler存在的问题也无法回避。

 

最后我们自然会想到OpenJDK的JDK Flight Recorder,简称JFR。AJDK8.5.10+和AJDK11支持JFR。JFR是JVM内置的诊断工具,类似飞机上的黑匣子,可以低开销的记录很多关键数据,存储到特定格式的JFR文件中,用这些数据可以很方便的还原应用启动过程,从而指导启动优化。JFR的缺点是有一定的使用门槛,需要对虚拟机有一定的理解,高级配置也较复杂,同时还需要搭配桌面软件Java Mission Control才能解析和阅读JFR文件。

 

面对上述问题,JVM工具团队进行了深入的思考,并逐步迭代开发出了针对启动过程分析的技术产品。

 

2) 解决方案

 

我们选择JFR作为应用启动性能剖析的基础工具。JFR开销低,内建在JDK中无第三方依赖,且数据丰富。JFR会周期性记录Running状态的线程的栈,可以构建CPU热点火焰图。JFR也记录了类加载、GC、文件IO、SocketIO、编译、VM Operation、Lock等事件,可以回溯线程的关键活动。对于早期版本JFR可能存在性能问题的特性,我们也支持自动切换到aync-profiler以更低开销实现相同功能。

 

为了降低JFR的使用门槛,我们封装了一个javaagent,通过在启动命令中增加javaagent参数,即可快速使用JFR。我们在javaagent中内置了文件收集和上传功能,打通数据收集、上传、分析和交互等关键环节,实现开箱即用。

 

我们开发了一个Web版本的分析器(或者平台),它接收到javaagent收集上传的数据后,便可以直接查看和分析。我们开发了功能更丰富和易用的火焰图和线程活动图。在类加载和资源文件加载方面我们也做了专门的分析,类似URLClassLoader在大量Jar包场景下的Class Loading开销大、Tomcat的WebAppClassLoader在大量jar包场景下getResource开销大、并发控制不合理导致锁争抢线程等待等问题都变得显而易见,未来还将提供评估开启CDS(Class Data Sharing)以及JarIndex后可以节省时间的预估能力。

 

3) 原理

 

当Oracle在OpenJDK11上开源了JDK Flight Recorder之后,阿里巴巴也是作为主要的贡献者,与社区包括RedHat等,一起将JFR移植到了OpenJDK 8。

 

JFR是OpenJDK内置的低开销的监控和性能剖析工具,它深度集成在了虚拟机各个角落。

 

JFR由两个部分组成:

 

第1个部分分布在虚拟机的各个关键路径上,负责捕获信息

第2个部分是虚拟机内的单独模块,负责接收和存储第1个部分产生的数据。

 

这些数据通常也叫做事件。JFR包含160种以上的事件。JFR的事件包含了很多有用的上下文信息以及时间戳。比如文件访问,特定GC阶段的发生,或者特定GC阶段的耗时,相关的关键信息都被记录到事件中。

 

尽管JFR事件在他们发生时被创建,但JFR并不会实时的把事件数据存到硬盘上,JFR会将事件数据保存在线程变量缓存中,这些缓存中的数据随后会被转移到一个global ring buffer。当global ring buffer写满时,才会被一个周期性的线程持久化到磁盘。

 

虽然JFR本身比较复杂,但它被设计为低CPU和内存占用,总体开销非常低,大约1%甚至更低。所以JFR适合用于生产环境,这一点和很多其它工具不同,他们的开销一般都比JFR大。

 

JFR不仅仅用于监控虚拟机自身,它也允许在应用层自定义事件,让应用程序开发者可以方便的使用JFR的基础能力。

 

有些类库没有预埋JFR事件,也不方便直接修改源代码,我们则用javaagent机制,在类加载过程中,直接用ASM修改字节码插入JFR事件记录的能力。比如Tomcat的WebAppClassLoader,为了记录getResource事件,我们就采用了这个方法。

 

整个系统的结构如下:

 

image.png 

相关文章
|
2月前
|
存储 数据采集 搜索推荐
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
本篇文章探讨了 Java 大数据在智慧文旅景区中的创新应用,重点分析了如何通过数据采集、情感分析与可视化等技术,挖掘游客情感需求,进而优化景区服务。文章结合实际案例,展示了 Java 在数据处理与智能推荐等方面的强大能力,为文旅行业的智慧化升级提供了可行路径。
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
|
2月前
|
存储 监控 数据可视化
Java 大视界 -- 基于 Java 的大数据可视化在企业生产运营监控与决策支持中的应用(228)
本文探讨了基于 Java 的大数据可视化技术在企业生产运营监控与决策支持中的关键应用。面对数据爆炸、信息孤岛和实时性不足等挑战,Java 通过高效数据采集、清洗与可视化引擎,助力企业构建实时监控与智能决策系统,显著提升运营效率与竞争力。
|
2月前
|
Java 大数据 数据处理
Java 大视界 -- 基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战(222)
本文探讨了基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战。文章分析了传统制造模式的局限性,介绍了工业互联网带来的机遇,并结合实际案例展示了 Java 在多源数据采集、实时处理及设备协同优化中的关键技术应用。同时,也深入讨论了数据安全、技术架构等挑战及应对策略。
|
6天前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
99 12
|
14天前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
67 1
|
2月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Java 大视界 -- Java 大数据机器学习模型在自然语言生成中的可控性研究与应用(229)
本文深入探讨Java大数据与机器学习在自然语言生成(NLG)中的可控性研究,分析当前生成模型面临的“失控”挑战,如数据噪声、标注偏差及黑盒模型信任问题,提出Java技术在数据清洗、异构框架融合与生态工具链中的关键作用。通过条件注入、强化学习与模型融合等策略,实现文本生成的精准控制,并结合网易新闻与蚂蚁集团的实战案例,展示Java在提升生成效率与合规性方面的卓越能力,为金融、法律等强监管领域提供技术参考。
|
2月前
|
存储 人工智能 算法
Java 大视界 -- Java 大数据在智能医疗影像数据压缩与传输优化中的技术应用(227)
本文探讨 Java 大数据在智能医疗影像压缩与传输中的关键技术应用,分析其如何解决医疗影像数据存储、传输与压缩三大难题,并结合实际案例展示技术落地效果。
|
2月前
|
机器学习/深度学习 安全 Java
Java 大视界 -- Java 大数据在智能金融反洗钱监测与交易异常分析中的应用(224)
本文探讨 Java 大数据在智能金融反洗钱监测与交易异常分析中的应用,介绍其在数据处理、机器学习建模、实战案例及安全隐私等方面的技术方案与挑战,展现 Java 在金融风控中的强大能力。
|
2月前
|
机器学习/深度学习 算法 Java
Java 大视界 -- Java 大数据机器学习模型在生物信息学基因功能预测中的优化与应用(223)
本文探讨了Java大数据与机器学习模型在生物信息学中基因功能预测的优化与应用。通过高效的数据处理能力和智能算法,提升基因功能预测的准确性与效率,助力医学与农业发展。

热门文章

最新文章