JIT的Profile神器JITWatch
    https://mp.weixin.qq.com/s/1r2NtTgsI63G_ICJMx6XxQ

    作者:kelthuzadx
    链接:https://www.zhihu.com/question/478887438/answer/2054092011
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    AOT编译即jaotc,可以将Java代码编成二进制,然后虚拟机直接用这些二进制,而不是在运行时花费时间用JIT编译。JDK10还尝试使用AOT背后的Graal代替HotSpot VM原有的C2 JIT编译器。但是它们两个的探索不算成功,首先用的不多,成功实践不多,关于这点可以问问自己有没有听过或者实际用过jaotc或者Graal JIT。
    其次Graal主要是Oracle Labs那边做开发,他们有一个仓库,相当于它们是JDK里面的Graal和AOT的上游,但是它们在JDK中的测试挂了没人修,也不太活跃,对JDK的代码演进会照成一定的困扰(小道消息可能还有派系斗争。。),基于总总原因,OpenJDK社区决定在JDK17中移除它们,由Oracle Labs在独立的仓库中维护。所以,OpenJDK社区确实已经彻底放弃基于Graal的AOT了,但是彻底放弃AOT这个技术本身也不一定,没准哪天就有个基于C2的AOT呢:P.
    是不是听起来感觉还有救?反正还有Oracle Labs仓库的继续维护,不慌。
    相关用户还是要慌一下,因为Oracle Labs也在它们的主线上移除了AOT,我们未来可能要永远告别jaotc了。至于Graal短时间内肯定还是有的,因为它是GraalVM安身立命的基础。
    但是对于函数计算、Serverless甚至更广阔的应用场景,快速启动、内存低占用这些仍然是刚需,我们仍然需要解决。
    —— 探索1
    很多应用启动性能Profiling显示JVM启动慢的主要原因是类的加载-链接-初始化这一套,它们占据了启动的绝大部分时间,JVM本身的启动是非常快的(-Xlog:startuptime)。对于类的三部曲,一个解决方案是AppCDS技术,OpenJDK对它做了很多工作,阿里JDK也对快速启动和AppCDS做了大量改造(所以,亲,看机会吗?
    回到技术本身。JVM之所以要加载-链接-初始化一个类,目的是让用户写的一个类Foo,在虚拟机里面能有个数据结构(即InstanceKlass)对应,这样虚拟机就可以通过它知道Foo实现了哪些接口,父类是谁等等。那么只要Foo类的字节码没有变过,我们跑多次应用,最终JVM里面的InstanceKlass就可以是一样的。这就是AppCDS的洞察,它可以让应用先 模拟 跑一次,把JVM里面已经走过加载-链接-初始化流程的InstanceKlass都存放到App.jsa文件里面,第二次 实际 运行时直接使用读App.jsa,拿到已经准备就绪的InstanceKlass直接用,省去了加载-链接的开销。
    —— 探索2
    注意AppCDS不能跳过初始化这个过程,这个过程需要另一项依赖AppCDS和G1的技术,即Pre-initialization提前初始化来部分解决。Pre-initialization是比较前沿的技术,也处于探索期间,最新的工作可以使这个技术适用于非G1的垃圾回收器。
    OpenJDK还针对AppCDS做了非常多的工作,比如DynamicAppCDS、AppCDS for Lambda/Proxy Classes,就不展开了。
    ——探索3
    除了VM上面做功夫之外,Linux CRIU技术也不容忽视。CRIU的介绍我就贴一点官网的:CRIU可以冻结运行中的容器或者某个单独的应用,然后把它的状态保存到磁盘上, 然后下次运行时读取磁盘的数据恢复执行。理想情况下,应用程序是无感知的,它不知道被暂停过。
    OpenJDK也在积极探索,前一段时间Azul的一位开发者(为macOS M1提供JDK的那个男人)提议成立CRaC项目(JDK中需要花费大量努力的方向或者技术探索都会创建一个项目,而不是在主线,比如ZGC,VectorAPI,Valhalla),这个技术旨在探索如何让CRIU和JVM更好的协作。该项目目前还在广泛讨(che)论(pi),参与者至少包括RedHat,Amazon,Azul。对于开发者,等就完事了。
    ——探索4
    再补充一点。
    OpenJDK还有一个Leyden项目,探索static-image技术,目标也是为了解决上面我们提到的Java启动慢、内存高占用等问题。
    那么static-image又是啥呢?假如有个void foo()方法,用传统的AOT如jatoc,会把foo编译成二进制,而static-image会把foo编译成二进制,同时再带上语言运行时(比如GC、线程模型等),最终得到一个“闭合的二进制世界”。static-image其实就是更广义的AOT。
    所以不要悲观,OpenJDK和GraalVM虽然都废弃了传统的AOT编译,但是两者都不约而同的探索static-image技术,OpenJDK这边是Leyden,GraalVM那边是SubstrateVM。不过我得吐槽一下。。Leyden雷声大雨点小,截至目前(2021.8.12)还没有实际的动静,GraalVM那边的SubstrateVM倒是做的风生水起。