Graal VM:云原生时代的Java( 三 )


直到Substrate VM出现,才算是满足了人们心中对Java提前编译的全部期待 。Substrate VM是在Graal VM 0.20版本里新出现的一个极小型的运行时环境,包括了独立的异常处理、同步调度、线程管理、内存管理(垃圾收集)和JNI访问等组件,目标是代替HotSpot用来支持提前编译后的程序执行 。它还包含了一个本地镜像的构造器(Native Image Generator)用于为用户程序建立基于Substrate VM的本地运行时镜像 。这个构造器采用指针分析(Points-To Analysis)技术,从用户提供的程序入口出发,搜索所有可达的代码 。在搜索的同时,它还将执行初始化代码,并在最终生成可执行文件时,将已初始化的堆保存至一个堆快照之中 。这样一来,Substrate VM就可以直接从目标程序开始运行,而无须重复进行Java虚拟机的初始化过程 。但相应地,原理上也决定了Substrate VM必须要求目标程序是完全封闭的,即不能动态加载其他编译期不可知的代码和类库 。基于这个假设,Substrate VM才能探索整个编译空间,并通过静态分析推算出所有虚方法调用的目标方法 。
Substrate VM带来的好处是能显著降低了内存占用及启动时间,由于HotSpot本身就会有一定的内存消耗(通常约几十MB),这对最低也从几GB内存起步的大型单体应用来说并不算什么,但在微服务下就是一笔不可忽视的成本 。根据Oracle官方给出的测试数据,运行在Substrate VM上的小规模应用,其内存占用和启动时间与运行在HotSpot相比有了5倍到50倍的下降,具体结果如下图所示:

Graal VM:云原生时代的Java

文章插图
启动时间对比
Graal VM:云原生时代的Java

文章插图
启动时间对比
Substrate VM补全了Graal VM“Run Programs Faster Anywhere”愿景蓝图里最后的一块拼图,让Graal VM支持其他语言时不会有重量级的运行负担 。譬如运行JavaScript代码,Node.js的V8引擎执行效率非常高,但即使是最简单的HelloWorld,它也要使用约20MB的内存,而运行在Substrate VM上的Graal.js,跑一个HelloWorld则只需要4.2MB内存而已,且运行速度与V8持平 。Substrate VM 的轻量特性,使得它十分适合于嵌入至其他系统之中,譬如Oracle自家的数据库就已经开始使用这种方式支持用不同的语言代替PL/SQL来编写存储过程 。
没有虚拟机的Java
尽管Java已经看清楚了在微服务时代的前进目标,但是,Java语言和生态在微服务、微应用环境中的天生的劣势并不会一蹴而就地被解决,通往这个目标的道路注定会充满荆棘;尽管已经有了放弃“一次编写,到处运行”、放弃语言动态性的思想准备,但是,这些特性并不单纯是宣传口号,它们在Java语言诞生之初就被植入到基因之中,当Graal VM试图打破这些规则的同时,也受到了Java语言和在其之上的生态生态的强烈反噬,笔者选择其中最主要的一些困难列举如下:
  • 某些Java语言的特性,使得Graal VM编译本地镜像的过程变得极为艰难 。譬如常见的反射,除非使用安全管理器去专门进行认证许可,否则反射机制具有在运行期动态调用几乎所有API接口的能力,且具体会调用哪些接口,在程序不会真正运行起来的编译期是无法获知的 。反射显然是Java不能放弃不能妥协的重要特性,为此,只能由程序的开发者明确地告知Graal VM有哪些代码可能被反射调用(通过JSON配置文件的形式),Graal VM才能在编译本地程序时将它们囊括进来 。这是一种可操作性极其低下却又无可奈何的解决方案,即使开发者接受不厌其烦地列举出自己代码中所用到的反射API,但他们又如何能保证程序所引用的其他类库的反射行为都已全部被获知,其中没有任何遗漏?与此类似的还有另外一些语言特性,如动态代理等 。另外,一切非代码性质的资源,如最典型的配置文件等,也都必须明确加入配置中才能被Graal VM编译打包 。这导致了如果没有专门的工具去协助,使用Graal VM编译Java的遗留系统即使理论可行,实际操作也将是极度的繁琐 。
{name: "com.github.fenixsoft.SomeClass",allDeclaredConstructors: true,allPublicMethods: true},{name: "com.github.fenixsoft.AnotherClass",fileds: [{name: "foo"}, {name: "bar"}],methods: [{name: "<init>",parameterTypes: ["char[]"]}]},// something else ……