GraalVM - 云原生时代的 Java 笔记

  • 前言
  • GraalVM 诞生的背景
  • GraalVM
  • Graal Compiler
  • Benefits of JIT
  • Creating a Native Image
  • 限制
  • Configuring Reflection Configuration
  • Static Configuration
  • Dynamic Configuration via Agent
  • Dynamic Configuration via Feature
  • Spring Graal Native



前言

Graal VM:云原生时代的 Java 笔记

GraalVM 诞生的背景

Java 在微服务时代宏观上的困境:

  • 一个事实: Java 总体上是面向大规模、长时间的服务端应用而设计的。严(luo)谨(suo)的语法利于约束所有人写出较一致的代码,利于软件规模的提升;即时编译器、性能制导优化、垃圾收集子系统等有代表性的特征都是面向程序长时间运行设计的,需要一段时间来达到最佳性能,也便于享受硬件规模提升的红利。
  • 一个矛盾: 在微服务的背景下, 提倡服务围绕业务能力构建, 不再追求实现上严谨一致; 单个微服务就不再需要再面对数十、数百 GB 乃至 TB 的内存; 有了高可用的服务集群, 也无须追求单个服务要 7*24 小时不可间断地运行, 它们随时可以中断和更新。但微服务对应用的容器化亲和度(包容量、内存消耗等)、启动速度、达到最高性能的时间等方面提出了新的需求, 这些又正好是 Java 的弱项。

问题的根源:

  • Java 是 VM Base 而不是 Native Base 的
  • Java 的代码域是动态的、开放的, 而不是静态的、封闭的

解决方案

  • 革命派: 直接革掉 Java 和 Java 生态的性命, 创造新世界, 譬如 Golang。
  • 激进派: 摒弃重负载的传统 Java 生态, 在 GraalVM 上另起炉灶开发新的 Java 应用, 譬如 Quarkus、Micronaut。
  • 温和派: 尽可能保留原有主流 Java 生态和技术资产, 尽可能通过技术手段自动化地把遗留代码升级成为 GraalVM Native 应用。
  • 保守派: 在原有的 Java 生态上做改进, 朝着微服务、云原生环境靠拢、适应, 譬如 CNFC Buildpack。

GraalVM

GraalVM 是 Oracle 新一代的多用途(Universal)、多语言(Polyglot)的虚拟机, 其中包括的主要技术组件有:

  • Increase application throughput and reduce latency
    高性能的即时编译器和提前编译器 Graal Compiler
  • Compile applications into small self-contained native binaries
    代替 HotSpotVM 的微型运行环境 SubstrateVM
  • Seamlessly use multiple languages and libraries
    以 Truffle 和 Sulong 为代表的中间语言解释器

Graal Compiler

  • 最初(2012 年之前)是在 Maxine 虚拟机中作为 C1X 编译器的下一代编译器而设计的, 所当然地使用 Java 语言来。
  • 高编译效率、高输出质量、同时支持提前编译(AOT)和即时编译(JIT), 同时支持应用于包括 HotSpot 在内的不同虚拟机的编译器。
  • 与 C2 采用一样的中间表示形式(Sea of Nodes IR), 后端优化上直接继承了大量来自于 HotSpot 的服务端编译器的高质量优化技术, 是现在高校、研究院和企业编译研究实践的主要平台了。
  • Graal Compiler 是 GraalVM 和 HotSpotVM (从 JDK10起)共同拥有的服务端即时编译器, 是 C2 编译器未来的替代者。
  • VM base VS Native Base
  • JIT(HotSopt) VS AOT(GraalVM Native)

Benefits of JIT

  • Profile-Guided Optimization, PGO
  • Aggressive Speculative Optimization
  • Link-Time Optimization

Creating a Native Image

  • 与原本 Java 一样编译程序
  • 使用 GraalVM 的 native-image 命令生成一个本地镜像(大坑警告)
    有限制, 并不能支持 Java 中所有的特性
    如果没有其他辅助手段的话, 需要大量的配置信息
    需要大量的 CPU 和内存资源

限制

  • Not Supported
    Dynamic Class Loading/Unloading
    Runtime Bytecode Generation
    InvokeDynamic Bytecode and Method Handles
    Finalizers
    Serialization
    Security Manager
    JVMTI、JVMCI, other native VM interfaces
  • Require Configuration
    Resource Access
    Reflection
    Dynamic Proxy(JDK, not CGLIB)
    JNI(Java Native Interface)
    JCA(Java Cryptography Architecture)

Configuring Reflection Configuration

  • reflection-config.json
[
	{
		name: "com.example.Something",
		allDeclaredConstructors: true,
		allPublicMethods: true
	},
	{
		name: "com.example.SomethingElse",
		fields: [
			{ 
				name: "value"
			}
		],
		methods: [
			{
				name: "<init>",
				parameterTypes: [
					"char[]"
				]
			}
		]
	}
]

Static Configuration

  • Point at the config files directly with native-image options:
-H:ReflectionConfigurationResources=/path/reflection-config.json \
-H:DynamicProxyConfigurationResources=/path/dynamic-proxies.json \
...
  • Or, place files in well known locations on the classpath:
META-INF/native-image/
META-INF/native-image/<groupId>/<artifactId>
...

Dynamic Configuration via Agent

  • Agent can assist in generating these files, available with GraalVM
$ java -agentlib:native-image-agent=config-output-dir=META-INF/native-image Demo
  • But…
    Doesn’t address initialization though, exercise all code paths(manually or via tests)

Dynamic Configuration via Feature

  • Create a class implementing the Graal Feature interface
    com.oracle.substratevm:graal-hotspot-library
  • Register new entries for reflection/resource/proxies/initialization handling
  • Add @AutomaticFeature to ensure picked up from classpath automatically

Spring Graal Native

spring-graalvm-native