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


    ​第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();
        ......
    }

    “这个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在云计算时代的危机暂时度过,未来它还会遇到什么挑战呢?

  • 相关阅读:
    JavaEE初阶:文件操作 和 IO
    AI 编程探索- iOS动态标签控件
    深度讲解TS:这样学TS,迟早进大厂【11】:类型断言
    pytorch线性代数的基本操作
    [每日两题系列]刷算法题咯~~
    睡眠不好怎么调理?
    11.30排序
    【重温基础算法】内部排序之归并排序法
    线程是如何在 6 种状态之间转换的?
    【C#】FileInfo类 对文件进行操作
  • 原文地址:https://blog.csdn.net/m0_73257876/article/details/126462408