04|从JIT到AOT:JVM编译器的云原生演进之路
字节码转化为机器码的发展历程
解释执行(Interpreter Execution)
即时编译(Just in Time Compilation)
- 深入了解
- 翻译
- 解释
- 总结
JVM编译器的演进历程经历了解释执行、即时编译和提前编译三个阶段。逃逸分析是JIT编译器的重要优化技术,通过判断对象是否逃出方法作用域来进行优化。适用场景包括局部变量的合理设计和使用,以及使用final关键字限制对象可变性等。JIT编译器的演进从C1、C2到Graal,Graal作为C2的接班者,在性能方面表现出色。另外,JVM在逐步从JIT编译向AOT编译转变,结合AOT编译可以提高程序的运行速度和启动速度,适应云原生和容器化环境。整体而言,JVM编译器的演进旨在提升字节码执行性能,逐步转变为依赖云原生时代提供的平台无关性解决方案。
《云时代的 JVM 原理与实战》,新⼈⾸单¥59
全部留言(13)
- 最新
- 精选
- 浩仔是程序员AOT还可以使用反射和动态代理吗?
作者回复: 这个问题非常好,使用AOT后无法直接使用反射等动态的特性,目前业内有两个解决方案分别是Spring Boot 3和Quarkus,他们提供了在使用AOT编译时处理反射和动态代理问题的解决方案。它们通过编译时生成配置、代码分析和优化等手段,实现了AOT编译和Java的动态特性的结合,我会在实战篇中详细介绍这部分,可以到时候关注下
2023-08-30归属地:广东27 - 临风java首先是通过javac编译成字节码,然后jvm才能通过执行字节码执行程序。jvm有两种执行方式,解释执行和编译执行,解释执行就是jvm直接翻译字节码为机器码运行,编译执行是jvm先将字节码编译成机器码并且缓存起来再执行。 很明显解释执行在第一次绝对是比编译执行快的,但如果一段代码执行的次数多了,那么编译执行的效率反而是比解释执行高了。所以jvm会将热点代码进行编译执行,而大部分代码仍然保持解释执行。这也是为什么Java需要运行一段时间才能达到性能巅峰的原因。 java使用c1(速度快、优化差、针对简单的逻辑)、c2(速度慢、优化好、针对复杂的逻辑)来进行编译,使用C++编写的,现在已经难以维护了。所以使用java推出了新的graal编译器代替c2编译器。这些编译器都属于JIT的范畴,都是在运行时去编译代码。 为了适应云原生的背景,java推出了aot,支持直接将java文件编译为二进制执行文件,使用graal VM代替jvm执行,实现了毫秒级的启动时间。由于没有了运行时,对整个java生态也提出了挑战,不过spring boot3已经率先支持了这一特性。 以上就是对本文的小结和自己一些简单的认识,如果有问题,还望老师指正。
作者回复: 总结的非常好👍🏻
2023-08-29归属地:广东5 - 追逐我的明天。关于逃逸分析 我想问个问题 作者原话:通过 JIT 我们能够确定哪些对象可以被限制在方法内部使用,不会逃逸到外部,然后可以对它们进行优化 但是下面的代码示例,引用肯定被传递到外面了,但是这段代码不还是被优化了嘛?我现在不太明白是传递到外面的 会被优化 还是不会被优化呢?
作者回复: 通过逃逸分析,JVM可以知道一个对象的使用范围,从而确定该对象是否可以"逃逸"出方法或线程等。如果一个对象只能从创建它的线程访问,那么就可以在栈上分配它的内存,而不是在堆上。这样可以避免后续的垃圾回收。对于上面的代码,经过逃逸分析,我们可以看到 `food` 对象只在 `main` 函数中使用,它没有被返回或者没有被其他线程引用,也就是说它的"使用范围"仅限于当前线程。因此,JVM就可以直接在栈上分配 `food` 对象的内存,从而提高程序性能。这是逃逸分析的基本思想,不过在实际的JVM实现中,这种优化可能并不会发生,因为只有当对象的大小和存活时间符合一定的条件时,才会将对象的内存分配从堆转移到栈。上述代码只是一个简单的示例,实际上JVM的发现过程可能比这个要复杂得多。
2023-08-31归属地:北京2 - 学无涯看老师发的jit对比aot的图,好像aot除了启动时间,其他方面都不如jit,是我理解错了吗?
编辑回复: 1. 峰值吞吐:AOT在运行时能够快速获取和执行代码,它的执行速度比JIT快。【一段时间内吞吐量的最大值,吞吐量越高,性能越好,所以图片显示AOT的条比JIT长】 2. 编译时间:因为是即时编译,所以JIT比AOT更快。 3. 包大小:因为AOT编译器需要将整个应用程序编译成可执行的二进制文件,所以AOT的包大小要比JIT大。 可能这张图片会让人产生一些误解,谢谢你的反馈,我们调整一下🌹
2023-08-28归属地:北京2 - 张申傲请教老师,使用 AOT 之后,一些基于字节码增强技术的框架是不是也无法支持了,比如 SkyWalking?
作者回复: 主流的 JVM 实现,如 HotSpot,还是首先优先支持 JIT 编译,只有在特定的场景下才会考虑使用 AOT 编译,因此,SkyWalking 在大部分情况下应该是可以正常使用的。但如果在你的应用中启用了 AOT 编译,并且发现 SkyWalking 无法正确工作,那么你可能需要考虑关闭 AOT 编译,或者寻找其他的解决方案。
2023-09-16归属地:北京1 - 小飞同学那为啥不直接用aot直接将字节码转换为机器码?jit编译器感觉可以废弃了
作者回复: Ahead-of-Time(AOT)编译并没有完全替代Just-in-Time(JIT)编译,而是和JIT编译结合在一起来使用。AOT编译和JIT编译各有优缺点。例如,AOT编译的优点在于它可以在应用程序运行前进行,从而减少了应用程序启动时的延迟。另一方面,它的缺点是,由于它不能利用到运行时的信息,例如运行时的类型信息和热点代码信息等,因此它往往无法达到JIT编译那样的优化程度。因此,目前的JVM实现往往结合使用AOT编译和JIT编译,以便同时利用二者的优点。一般的做法是,在应用程序启动时,先使用AOT编译生成的代码,然后在运行过程中,通过JIT编译对热点代码进行更深度的优化。这种做法既可以减少应用程序的启动延迟,又可以保证应用程序运行时的高效性能。
2023-09-08归属地:浙江1 - 小麦为了从 JIT 过渡到 AOT,JVM 将字节码与 AOT 编译相结合。在 JIT 编译运行时,JVM 会监视代码的执行情况并收集相关的运行时信息,然后将这些信息传递给 AOT 编译器。AOT 编译器会利用这些信息对字节码进行优化,并生成可执行的本地机器代码。这样,当相同的代码再次执行时,就可以直接使用 AOT 编译得到的机器代码,而无需再次启动 JIT 编译。 从此描述中,没看出在 JIT 编译器识别出热点代码后,交给 AOT 编译器的好处是什么
作者回复: 这两种技术各有优势,也各有劣势。对于JIT,每次程序启动时都需要花费时间进行编译,甚至在运行时也需要不断编译新发现的热点代码。而AOT虽然可以避免运行时编译带来的延迟,但是因为无法利用运行时信息进行优化,所以其生成的代码的效率往往不如JIT。但是,如果我们能够找到一种方法,利用运行时的信息进行AOT编译,那么就可以把两者的优势结合起来。这就是JVM将字节码与AOT编译相结合的原因:它先使用JIT编译器进行编译,找出热点代码并进行优化,然后把这些信息传递给AOT编译器,让它对字节码进行进一步的优化。通过这种方式,我们既可以利用运行时的信息进行优化,获得高效的机器码,又可以避免运行时编译带来的延迟,从而实现更快的程序启动和执行速度。
2023-09-15归属地:广东3 - geektime_zpf“为了从 JIT 过渡到 AOT,JVM 将字节码与 AOT 编译相结合。在 JIT 编译运行时,JVM 会监视代码的执行情况并收集相关的运行时信息,然后将这些信息传递给 AOT 编译器。AOT 编译器会利用这些信息对字节码进行优化,并生成可执行的本地机器代码。”,老师好,文中这几句不理解,jit收集的运行时信息,怎样传递给aot?
作者回复: JIT 编译器在运行时收集的信息,可以通过一些内部机制传递给 AOT 编译器,用于对字节码进行优化。具体来说,这个传递的过程可能是通过 JIT 编译器将信息存储在一些特定的数据结构中,这些数据结构可以被 AOT 编译器读取。也可能是通过 JVM 提供的一些 API,允许 AOT 编译器查询 JIT 编译器的信息。然后,AOT 编译器就可以利用这些信息进行优化了。 例如,如果 JIT 编译器收集到的信息显示某个方法被频繁调用,那么 AOT 编译器就可能会针对这个方法进行特别的优化,使其运行更快地优化方法的执行(比如,使用内联、循环展开、去除死代码等手段),并把优化后的代码缓存起来,下次直接使用。
2023-09-06归属地:广东 - Levi使用 final 关键字来限制对象的可变性,这样 JIT 编译器更容易进行逃逸分析和优化。 老师这句话不太理解,能解释一下吗请问
作者回复: 如果在定义一个对象时,用final关键字来修饰它,那么这个对象一旦被初始化后就不能再被修改,它的内部状态是不可变的。这样,JIT编译器在进行逃逸分析时,就可以更准确地判断出哪些对象没有发生逃逸,然后对它们进行优化,提升程序的运行效率。
2023-08-29归属地:北京 - peter请教老师几个问题啊: Q1:AOT难道没有运行时吗? 本课的第二张图,就是编译的那个图,AOT只有编译时,难道没有运行时吗? Q2:AOT提前编译,编译的时候需要选择目标平台吗?比如,目标是Linux或Windows。 Q3:AOT必须与JIT结合吗?从文中看,好像AOT是基于JIT才能工作。 Q4:AOT与JIT的对比图中,吞吐量这一项,AOT的条比JIT的短, 这个正确吗?
作者回复: A1:AOT(Ahead-of-Time Compilation)是指在运行程序之前,就已经将代码完全编译成本地机器码,所以的确主要关注的是编译时。然而,运行时的一些相关特性(比如反射,动态加载等)可能被限制或者需要一些特殊的处理。 A2:是的,AOT编译需要选择目标平台或者特定的运行环境。因为它直接将代码编译成特定平台的机器码,所以需要知道目标平台的硬件和操作系统信息。 A3:AOT并不必须和JIT结合。它们是两种截然不同的技术,各有各的优缺点。但在一些实现方案中,它们可能会结合使用,比如在Java的HotSpot虚拟机中,它支持一种混合模式,在程序启动初期使用解释执行,然后根据运行时的信息选择热点代码进行JIT或AOT编译,以提高效率。 A4:在AOT和JIT的对比中,AOT的吞吐量可能会小于JIT。因为AOT在程序运行前就已经进行了全部编译,初始启动时性能较好,但由于缺乏运行时信息,可能无法对特定的运行时行为进行优化。相反,JIT可以在运行时针对实际代码的行为进行优化,因此在运行一段时间后,其性能可能会优于AOT,吞吐量也就相应更高。同时,这一点也会受到实现方式等多种因素的影响,并不是绝对的。
2023-08-28归属地:河南