NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

从本地原生到云原生,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:435409

评论 2 条评论

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

粪菌移植的背后,肠道菌那些你不知道的事儿

脑极体

学生试卷&答题结构redis存储

Nico

Leetcode 题目解析:279. 完全平方数

程序员架构进阶

算法 LeetCode 动态规划 10月月更

并发相关的性质学习笔记

风翱

并发 10月月更

linux中vi,vim操作技巧

入门小站

Linux

在线下划线转驼峰,驼峰转下划线工具

入门小站

工具

应用出海,如何使用苹果 CallKit 提升网络通话体验

融云 RongCloud

音视频 出海社交

面试官:你说说ThreadLocal为什么会导致内存泄漏?

长河

Java

实践篇 -- Redis客户端缓存在SpringBoot应用的探究

binecy

缓存 springboot redis sentinel

存量时代会员深度运营逻辑

boshi

深度思考 运营

gRPC,爆赞

AlwaysBeta

golang 编程 gRPC 后端 Go 语言

「架构师教程」二十年架构师「马士兵」大牛的Java高级架构师教程

Java 编程 程序员 IT 计算机

Node.js 日志之 winston 实践

devpoint

nodejs winston logger 10月月更

阿里架构师总结Go语言和java语言之间的对比联系

hanaper

前后端、多语言、跨云部署,全链路追踪到底有多难?

阿里巴巴云原生

阿里云 云原生 全链路追踪

ToB产品如何自传播(下)

石云升

产品经理 产品思维 10月月更

隐蔽的角落-这次我们只聊Cilium IPAM

Lance

Prometheus 基础查询(一)

耳东@Erdong

Prometheus 10月月更

Serverless 工程实践 | 零基础上手 Knative 应用

阿里巴巴云原生

阿里云 Serverless 云原生 Knative

业界良心啊!第五次更新的Spring Cloud Alibaba升级太多内容

Java 编程 程序员 IT 计算机

面试作弊神器?!阿里P8亲自撰写的这份Java最新面试手册

Java 程序员 架构 面试 后端

SSRF漏洞实例分析

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞分析

Facebook宕机事故,暴露了上云不是唯一的答案

脑极体

SpringBoot 实战:在 RequestBody 中优雅的使用枚举参数(原理篇)

看山

Java Spring Boot Effective Spring 10月月更

Alibaba最新微服务持续集成,内含(Jenkins+Docker+Spring Cloud+K8S)

Java 架构 面试 程序人生 编程语言

Groovy 记录(2)-CompilationUnit

春秋易简

【Vuex 源码学习】第十二篇 - Vuex 插件机制的实现

Brave

源码 vuex 10月月更

拿蚂蚁offer,全靠阿里P8大牛总结的Java架构开发手册

Java 编程 程序员 架构 面试

双非学历为进大厂天天刷Java面试题,面试却履败,原因竟是算法?

Java 编程 程序员 架构 IT

Groovy记录(1)-GroovyClassLoader

春秋易简

Groovy

趣说Node.js的回调函数

Regan Yue

node.js JavaScrip Regan Yue 10月月更

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