写点什么

从本地原生到云原生,Alibaba Dragonwell 静态编译的实践与挑战

  • 2019-10-25
  • 本文字数:3083 字

    阅读完需:约 10 分钟

从本地原生到云原生,Alibaba Dragonwell静态编译的实践与挑战

近些年来,云原生(Cloud Native)的概念席卷业界,其核心元素之一就是如何提高云上业务的响应速度。随着应用微服务化、容器化程度的不断提高,应用的执行响应速度也在不断提升,逐渐触及到了Java程序速度提升的天花板——Java 自身的启动运行开销。


虽然 Java 从最初的单独解释执行演进到 JIT(实时编译)和 AOT(提前编译)后,在运行时 peak performance 和冷启动性能方面都取得了大幅进步,但如虚拟机启动、代码解释执行、JNI 调用、反射开销、静态初始化检查开销等 Java 虚拟机和运行时本身的耗时点始终存在,并且耗时占比随着应用程序运行时间的降低而愈加突出。要进一步提高云原生应用的启动执行速度,突破 Java 自身的启动运行开销性能瓶颈,就必须在传统的 Java 程序编译启动执行过程之外,另辟蹊径。

Java 静态编译简介

Java 静态编译技术是一种激进的 AOT 技术,通过单独的编译阶段将 Java 程序编译为本地代码,在运行时无需传统 Java 虚拟机和运行时环境,只需操作系统类库支持即可。目前较成熟、受到业界广泛关注的开源 Java 静态编译器是由 Oracle 开发的GraalVM SubstrateVM(以下简称 SVM),图 1 展示了目前基于 SVM 的阿里巴巴静态编译项目的基本编译过程。


SVM 遵循封闭性假设(closed-world assumption, 即运行时所需的所有内容必须在编译时提供),将应用程序代码及其所依赖的三方 jar 文件和Alibaba Dragonwell代码编译为本地机器码,同时也将用 Java 编写的 Runtime 编译为机器码,两者被编到一个 elf 可执行文件或 so 共享库文件中,实现代码的自举。运行阶段只需直接执行 elf 文件,或者在其他 C/C++程序中调用 so 文件中发布的 API 即可。



图 1:SVM 静态编译基本过程


可以认为静态编译技术实现了 Java 语言与原生 Native 程序的“合体”,将原本的 Java 程序编译成为了一个自举的具有 Java 行为的原生 Native 程序,由此兼有 Java 程序和原生 Native 程序的优点。

Alibaba Dragonwell 静态编译实践

极速启动:Meta 节点应用 Serverless 化

从 Java 程序角度看,经过静态编译后 Java 程序具有更快的启动速度、更低的内存占用、更小的发布体积,在云上部署时从服务拉起到响应用户的请求所需的时间更短,非常适合云原生的需求。


以蚂蚁开源中间件项目 sofastack 的服务注册中心Meta节点应用为例,该项目验证了静态编译在 Serverless 应用上的可行性。图 2 展示了以传统 Java 方式运行和静态编译程序运行的几项数据的比较。



图 2:Sofastack 注册中心 Meta 节点静态编译前后性能对比图


左上图的服务启动时间指服务完全启动到可以接受用户请求的状态所需的时间;右上图的可执行文件大小指包含了运行 Java 程序所需的所有依赖资源的 fat jar 包和静态编译出的 elf 可执行文件的大小比较,虚线框代表没有显式打入 fat jar 的 Alibaba Dragonwell;底图的运行时内存消耗则是以相同压力请求测试时,两个版本应用各自的内存使用情况。从图中可见静态编译的代码在这些指标上较传统 Java 程序有质的提升,服务启动时间降低了 17 倍,可执行文件大小降低了 3.4 倍,运行时内存降低了一半。

多语言支持:RocketMQ 客户端应用

从 Native 原生代码角度看,在静态编译的帮助下开发人员可以用 Java 编写出 Native 代码,较以往的 C/C++开发效率更高、为同一业务维护多个语言版本的成本更低。


在这方面的探索中,阿里巴巴发布了静态编译版本的 RocketMQ 客户端,为 RocketMQ 进一步 Serverless 化提供了语言无关的运行时保障,实现了一套内核的多语言支撑。图 3 展示了 RocketMQ 客户端的多语言维护的示意。



图 3:RocketMQ 客户端多语言实现示意图


在引入静态编译技术之前,同一需求需要用两种语言分别实现不同的 SDK,最终运行在不同的环境中,开发和维护成本高。在静态编译的支持下,同一需求只用 Java 单一语言开发,然后沿图中绿色路径静态编译得到 Native Code。当然此外还需要一部分适配工作,将拟暴露的 API 用 SVM 定义的 C-Java 转化协议包装起来。


与先前单独编写 C++客户端相比,静态编译方案给多语言客户端的快速开发和后续管理带来了诸多优势:


  • 基于 Java 统一内核实现,避免功能重复开发;

  • 面向接口设计开发,内核实现插件化加载;

  • 功能升级只需分发内核,用户无感知;

  • 客户端实现闭包,运行时无任何第三方依赖。


目前静态编译版本的 RocketMQ 客户端已稳定可靠地服务阿里巴巴集团内部以及阿里云超过 10+ 业务场景,包括网络延迟在内的客户端启动时间相比 Java 原生客户端提升 30%,达到与 C++版应用同等的启动性能。

静态编译的挑战和 Alibaba Dragonwell 的应对

然而世界上没有银弹,静态编译技术也不例外。兼有 Java 和原生 Native 代码两者的优势是以牺牲部分 Java 动态特性为代价换取来的,表格 1 列出了在 SVM 中受限制的 Java 特性。封闭性假设要求在编译时必须获取运行时所需的全部信息,这与 Java 的动态性相矛盾,是静态编译对传统 Java 限制最大的部分。代码 Native 化是指由于丢弃了 Bytecode,传统上基于 Bytecode 的特性便不再被支持。缺少工具指缺少调试静态编译后代码的工具,如 Heap Dump、Thread Dump、代码调试等功能在 SVM 社区版中都没有提供。


原因分类存在限制的特性具体解释
封闭性反射、动态类加载、动态代理、JNI反射、序列化反序列化、MethodHandler编译时需要完全掌握运行时的信息
代码native化插桩、JVMTI、agent编译为本地代码后已不存在bytecode
缺少工具Heap dump,Thread dump、调试部分存在于企业版中,社区版为提供


表格 1:SVM Java 特性限制一览


可以看到 SVM 静态编译仅支持了传统 Java 特性的子集,而且对生产级的应用还缺少良好的工具支持。阿里巴巴正在结合自己的业务场景与 SVM 开源社区紧密合作,以 Alibaba Dragonwell 和 SVM 为基础,积极探索如何缩小静态编译和传统 Java 之间的鸿沟。


一方面逐步扩展静态编译能够支持的特性子集,使静态编译能够适用于更多的业务场景。比如针对 JDK 原生序列化反序列化的需求,通过预执行输出动态加载的类,将其加入静态编译的代码范围中,并自动生成需要的反射配置信息提供给编译器,最终实现对序列化反序列化特性的支持。另一方面探索适合面向静态编译的 Java 编程模型,帮助开发人员便捷地开发出适合静态编译的 Java 程序。


阿里巴巴对 Alibaba Dragonwell 进行静态化剪裁,一边定义了使用反射、动态类加载等不完全支持特性时的规则,并通过 javac 做强制检查。一边将静态编译完全不能支持的特性从 Alibaba Dragonwell 中去除,帮助开发人员以静态编译的思维开发新的 Java 程序。最后增加了对 Thread dump,Heap dump 支持,并开发了针对阿里巴巴业务场景的更有效的 GC 算法。这些对业务场景的不断实验和打磨,在成熟后也会贡献反哺到社区。

总结

静态编译技术是一种兼具传统 Java 程序和本地原生代码程序二者优点的技术。以静态编译的原生代码助力云原生应用,既保持了传统 Java 的开发流程和效率,又消除了 Java 自身固有的性能瓶颈,显著改善服务拉起响应请求的速度,阿里巴巴也通过自身实践,证明了静态编译技术在实际生产中的可行性。


在长期的 Java 开发实践中,阿里巴巴对 OpenJDK 不断优化总结开发出了其下游 Alibaba Dragonwell JDK,现已运行在 100,000+服务器上,为阿里巴巴的各项业务提供基础服务。相信随着 Alibaba Dragonwell 静态编译版本进一步成熟,将能够支持更多原生代码应用部署上云。


作者介绍:


林子熠,阿里巴巴集团 JVM 团队技术专家。上海交通大学软件工程专业工学博士,毕业后加入华为编译器与编程语言实验室参与方舟编译器的研发工作,现于阿里巴巴 JVM 团队主要负责 Java 静态编译技术在 Java 服务端的开发与应用。


2019-10-25 14:435750

评论 2 条评论

发布
用户头像
看起来牛逼,性能提升上来,直接Java能干很多活了
2019-11-06 16:03
回复
后续应该缺少架构方面的内容了吧,基本上不能反射。更多应该关注到业务层了,简化了架构部分
2020-01-04 16:34
回复
没有更多了
发现更多内容

【终极预告】Apache ShardingSphere Dev Meetup 彩蛋篇

SphereEx

开源社区 ShardingSphere Meetup SphereEx 热门活动

zookeeper原理篇-Zookeeper选举过程分析,深入linux内核架构pdf下载

Java 程序员 后端

Zookeeper用作注册中心的原理,张孝祥jsp视频教程

Java 程序员 后端

Vue 生命周期 钩子函数,mybatisdao接口工作原理

Java 程序员 后端

“打工人”都在用的邮件使用规范,入职3个月的Java程序员面临转正

Java 程序员 后端

vue移动端自适应,mybatis面试问题

Java 程序员 后端

windows下nginx的安装及使用,linux实用教程第三版pdf

Java 程序员 后端

“穷苦乡村”小伙就得安于现状,你掌握了多少?

Java 程序员 后端

zabbix监控nginx、mysql、java应用,64位java8百度云盘

Java 程序员 后端

技术分享| RTC通讯中常用的音频格式

anyRTC开发者

音视频 WebRTC RTC 语音通话 音频格式

“情商比智商重要”,java面试代码题

Java 程序员 后端

《JVM系列》 第五章 -- 堆空间与对象分配,springboot项目分层架构

Java 程序员 后端

Vue学习之事件修饰符,java后端开发入门

Java 程序员 后端

“一学就会”微服务的架构模式,一名毕业三年的女程序媛面试头条经验

Java 程序员 后端

“ShardingCore”是如何针对分表下的分页进行优化的,深入理解linux内核架构

Java 程序员 后端

“数组&方法”常见知识分解,简述java编译原理

Java 程序员 后端

《Spring实战》读书笔记-第3章 高级装配,全网最具深度的三次握手、四次挥手讲解

Java 程序员 后端

Vue 数组操作,java基础教程百度网盘

Java 程序员 后端

xxl-job 源码运行解析,java基础编程视频

Java 程序员 后端

yum安装ansible报错如何解决,自定义线程池面试题

Java 程序员 后端

Zookeeper 集群部署的那些事儿,消息队列rabbitmq面试

Java 程序员 后端

ZooKeeper实现生产-消费者队列,万字长文总结Java多进程

Java 程序员 后端

Vue学习之v-if和v-for指令,tomcat常见面试题

Java 程序员 后端

vue遇到的坑,linux网络编程pdf百度云

Java 程序员 后端

WPF学习——依赖项属性,中软国际java面试流程

Java 程序员 后端

zookeeper分布式锁,java开发技术教程

Java 程序员 后端

《大型数据库技术》MySQL的进阶开发技巧,java基础知识重点总结pdf

Java 程序员 后端

Vue学习之自定义指令,宅家36天咸鱼翻身入职腾讯

Java 程序员 后端

requests库与 lxml 库常用操作整理+总结,爬虫120例阶段整理篇

梦想橡皮擦

11月日更

XXL-Job启动源码详解,Java日常开发的12个坑,你踩过几个

Java 程序员 后端

ICCV 2021人脸鉴伪比赛全赛道冠军!AI反诈这块,百度算是弄明白了

科技热闻

从本地原生到云原生,Alibaba Dragonwell静态编译的实践与挑战_服务革新_林子熠_InfoQ精选文章