深圳幻海软件技术有限公司 欢迎您!

Java要抛弃祖宗的基业,Java程序员危险了!

2023-02-28

​第11代Java国王坐在宝座上,俯视着臣民。经过历代国王的励精图治,他的Java帝国正处于巅峰状态。一群大臣看到新王登基,马上上来拍马屁。“从后端到手机端,从手机端到大数据,帝国疆域无边无际。”线程大臣率先定了基调。“Java是企业级应用无可撼动的霸主,生态环境极大丰富。Spring已经统治了后端

​第11代Java国王坐在宝座上,俯视着臣民。

经过历代国王的励精图治,他的Java帝国正处于巅峰状态。

一群大臣看到新王登基,马上上来拍马屁。

“从后端到手机端,从手机端到大数据,帝国疆域无边无际。” 线程大臣率先定了基调。

“Java是企业级应用无可撼动的霸主,生态环境极大丰富。Spring已经统治了后端开发。” 年迈的JVM大臣居然夸起Spring来!

“Java虚拟机性能强大,其他语言虚拟机都是玩具。”  Spring大臣赶紧投桃报李。

......

都是一些听过几百遍的、老掉牙的东西。

国王听得有些烦,挥手让众人退下。

他决定带几个保镖,微服出宫,到外边亲自走一走,看一看。

1.微服私访

走出都城大门,国王看到了一望无际的代码田地。

烈日下,无数的Java码农在这里辛苦劳作,CRUD的劳动号子响彻云霄。 

国王走近一看,果然,码农们用的工具都是SpringBoot和Spring Cloud,看来大臣所言不虚。

前面的大树下,一个中年人开着小茶铺,几个码农聚在那里,一边休息喝水、一边乘凉聊天。 

国王悄悄走近。

中年人打着蒲扇,笑眯眯地说:诸位,你们知不知道,Java已经大祸临头,你们有可能要失业了。

一个戴着厚厚眼镜的码农笑得把茶都喷了出来:哈哈哈,危言耸听,这怎么可能?

中年人慢悠悠地说:时代变了,原来的Java特别适合大规模的服务器端应用,尤其擅长时间高性能运行。现在是云计算时代,微服务时代,有了容器,集群,服务可以随时重启,并且微服务越来越小,用什么语言都可以。

另一个花格子衬衫码农说:那也可以用Java写啊,SpringBoot挺好的啊,约定重于配置,内置服务器,一个jar包就跑起来。 

其余几个码农纷纷附和,国王也暗自点头。 

中年人笑道:云端应用要求1. 镜像小  2. 启动速度快,即起即用。Java能做到吗?

厚眼镜码农说:嗯,Java的docker镜像动辄上G, 冷启动实在太慢了,每次都得等半天!

花格子衬衫说:还有Spring启动时用了太多的反射黑魔法,启动速度更慢。

中年人说道:这就对了,我带着小茶铺游历过Python王国、JavaScript王国,Go王国,人家那里就没有这样的问题,非常适合云端应用,你们不妨去看看啊。

一番话说得这几个Java码农动了心,开始窃窃私语,打探去那些王国的道路。

国王意识到这个中年人来者不善,给保镖使了个颜色。

保镖掀翻小茶铺,扭起中年人就走,留下几个码农目瞪口呆。

2.三个计策

国王召来Spring大臣和JVM大臣,一起审问这个中年人。

国王:你是何人,为什么在那里危言耸听、鼓惑我朝年轻人?

中年人:小民说的都是事实啊,陛下,您可能被蒙蔽了,外界正在发生翻天覆地的变化啊,Java如果不与时俱进,岌岌可危啊。

Spring大臣和JVM大臣互相看了一眼,意味深长。

国王倒不在意,问道:你有什么建议? 

中年人:小民有一个上策、中策和下策,陛下想先听哪一个?

国王:哦?三个计策?先说说下策。

中年人:下策自然是保留现状不变。

Spring大臣:相当于没说,中策呢?

中年人:中策就是改Spring,Spring应用在启动时会扫描代码中的bean,然后用反射的方式注册bean,这种做法的耗时与应用的代码量成正比,所以启动性能会很差。

如果在编译时把反射转化为直接调用的类,将会大幅提升应用的启动速度。我的研究显示,这种办法至少可以将成本降低50%,并且民间已经出现了一个叫做Micronaut的框架,它已经实现了编译期的依赖注入!

Spring大臣一听这家伙要把自己干掉,大惊失色,赶紧跪倒。

他先回顾了祖上如何用SpringMVC干死Struts的英勇事迹,又不动声色地提起自己如何与时俱进,用SpringBoot、Spring Cloud,Spring WebFlux在微服务时代和反应式编程时代勇立潮头。希望Java国王能念起旧情。

国王眼珠一转,看了一眼JVM大臣:好吧,也许这种办法能提升Spring应用的启动速度,但是据我所知JVM的启动速度也很慢,这又该怎么办?

中年人:这就是我要说的上策了,抛弃JVM,把Java程序编译成本地代码来执行!

“大胆!你这是要革命,要谋反!” JVM大臣忍不住了。

“陛下,这等狂悖之徒,拉下去问斩吧!” Spring大臣也立刻拱火。

国王心里很清楚,二十多年了,Java帝国最厉害的无过于字节码和JVM,如今ZGC垃圾回收器停顿时间不超过10ms,停顿时间还不会随着堆的增大而增大,JVM的JIT也炉火纯青,在运行时找到最热点的代码,编译成本地二进制执行,效率直逼C语言!

相比之下,JavaScript和Python虚拟机能叫虚拟机吗?玩具而已!它们怎么不强调自己的停顿时长?

不过这个计策倒是非常大胆,云计算时代,真的需要JVM吗? 

国王陷入沉思。 

3.抛弃JVM

JVM大臣看到国王不说话,又描述了一遍Java程序的生命周期。

  • JVM初始化 
  • 应用初始化
  • 应用预热
  • 应用稳定
  • 关闭

每个阶段都有着重要使命,尤其是应用预热的时候,会把Java字节码编译成本地代码。

“如果抛弃JVM,前辈们所做的所有努力都不复存在!这会动摇我Java帝国的国本啊!” JVM大臣伏地干嚎。

Java程序监控、扩展、jstat、jstack、jmap都用不了了。

调试的时候,也只能用复杂的GDB汇编调试,非常麻烦。

但是编译成本地代码,好处也非常明显,没有冷启动问题,启动即巅峰。

看到国王依然没有反应,JVM大臣决定抛出杀手锏:

“陛下,我Java帝国之所以能称雄世界,关键就是生态极其丰富,框架和类库覆盖了后端开发的所有方面。”

“而这些框架和类库中在大量地使用反射,甚至用动态代理在运行时动态生成字节码,换句话这些东西在编译时根本无法确定,只有到运行时才能确定。”

“举个例子,对于Class.forName("x.y.z")这样的代码,如何编译时就把它变成成本地代码?” 

姜果然是老的辣,JVM大臣一下子就抓住了最关键的点,把皮球踢给了中年人。

没想到中年人胸有成竹:“这非常简单,在做静态代码分析的时候我会发现x.y.z是个需要被装载的类,然后把它也编译成本地代码!”

“那如果这里不是个字符串的值,而是一个变量呢?Class.forName(someClassName)”  JVM老头得意地笑,他早就挖好了坑。 

“那就没办法了,只好让用户在配置文件中告诉我们哪些类需要编译成本地代码了。”

“哈哈哈,说得轻巧,一个框架用了那么多反射,你让用户在配置文件中全部提前告诉你,怎么可能?”

中年人不甘示弱:“那我可以开发一个程序,让用户的程序运行一遍,我的程序监控用户的程序哪些地方用了反射,然后自动生成配置文件!”

“程序那么多分支,你运行一遍就能找到所有用到反射的地方?”

JVM大臣转向国王,斩钉截铁地说:“陛下,此法断不可行。”

“寡人觉得这其实就是不满足封闭性原则。除了反射之外,还有动态代理,JNI,序列化等,当Java代码使用这些特性的时候,静态编译就会遇到问题,需要想变通办法,而变通办法又无法覆盖所有情况。”

国王果然是国王,高屋建瓴。

“陛下真是英明,一下子就上升到了理论层面,我等望尘莫及。” JVM赶紧拍马屁。

4.编译

“陛下,把这个散播谣言,鼓惑人心的家伙拉下去宰了吧!” Spring大臣提醒道。 

“虽然Java的动态性无法完美满足封闭性原则,但是静态编译确实是非常诱人,你说说,具体怎么做。” 国王不理Spring大臣,继续询问中年人。

“这个嘛,小民有个基本的思路,就是由用户指定程序入口,嗯,相当于main函数,然后静态编译器从这里开始分析程序的可达范围,把所有的可达的函数和一个小的运行时支持代码编译成native image。”

“可笑啊可笑,你难道忘记了Java是个面向对象的语言,多态无处不在?” JVM大臣讽刺。

“我给你举个例子,看看你怎么做静态分析。”

void process(List employees){
    int size = employees.size();
    ......
}
  • 1.
  • 2.
  • 3.
  • 4.

“这个List是JDK的一个接口,JDK有很多实现类(ArrayList,LinkedList,Vector等),我们的项目也有很多自定义的List实现类,employees的实际类型只能在运行时确定,你的静态分析如何确定呢?”

“你不会把List的所有实现类都给编译成二进制代码吧?”  Spring大臣马上添油加醋。

“如果是这样的函数 void process(Object o) ,Object是所有类型的根,难道你要编译所有的类?哈哈哈!” JVM大臣不由得大笑起来。 

“那肯定不行,我有个独门绝技,叫‘指向性分析’,可以在不运行程序的情况下,找到一个类型变量在运行时的可能类型。”  中年人不慌不忙。

指向性分析?Spring大臣和JVM大臣再次对视,他们明白这位中年人不会多说了。

国王盯着这位中年人,问道:“你叫什么名字?”

“小民叫Graal。”

国王心里盘算起来。

云计算时代,容器技术的出现,write once, run anywhere已经不重要了。

相反,Java确实面临着镜像大,冷启动慢的严峻挑战。

把Java代码编译成本地代码,要抛弃祖宗的基业,但可能是破局的关键。

自己作为新一代国王,坚决不能吃老本,更不能成为亡国之君,所有可能的方向都要尝试。

想到此处,国王对中年人说:“好吧Graal,寡人已经明白你的意图,现在给你一队人马,专门研究静态编译技术!Spring大臣你要密切配合!”

5.尾声

几个月后,中年人推出了一个新的虚拟机,叫做GraalVM,这个VM野心极大,不仅实现了把Java编译成本地代码,还支持JavaScript, Ruby, R,Python等语言。

虽然Spring大臣不太情愿,但是国王的圣旨不可违抗,他再次与时俱进,配合GraalVM推出了SpringNative ,把Spring应用编译成了原生镜像。

SpringNative启动时间提升了50倍,并且启动即巅峰,内存占用减少了5倍。

Java在云计算时代的危机暂时度过,未来它还会遇到什么挑战呢?