【JVM】04. 编译运行(一):后端编译

in study •  9 months ago 

本专题,我们将聚焦于JVM的后端编译过程,这是提高Java程序运行效率的核心环节。本文将详细介绍后端编译的执行方式、即时编译中的C1、C2编译器,还有分层编译的策略。

1 编译运行概述

Java 程序的编译过程是分为两部分:

  • 前端编译:将java文件编译成为class文件中的字节码指令,是在 JVM 虚拟机之外执行的,注重逻辑
  • 后端编译:将class文件的字节码指令编译成操作系统能识别的机器指令,需要进入到 JVM 虚拟机,也是执行引擎要做的事,注重性能。我们将重点介绍后端编译。

2 后端编译

后端编译有两种方式:

  • 解释执行:来一条字节码指令就翻译一次,早期的JVM执行引擎就是这么做的。这种方式需要在上层语言和机器码之间经过中间一层JVM字节码的转换,效率也因此比不上C/C++。这种方式的编译器又叫做解释器
  • 编译执行先编译后执行。将字节码指令,提前编译成机器码放到缓存里,执行的时候直接从缓存中查,以此来提升执行效率。
    • 即时编译JIT:在运行时,将热点字节码指令编译成本地机器码
    • 提前编译AOT:在运行前,将字节码指令编译成本地机器码

2.1 编译方式

HotSpot默认采用的混合执行模式,可以使用java -version可以进行查看,java -Xint -version切换为编译执行,java -Xcomp -version切换为解释执行

查看JVM代码执行模式.png

为什么JVM不直接采用性能明显更高的编译执行模式呢?

  • 内存压力大:随着编译执行将越来越多的代码编译成本地代码,会消耗更多的内存。参数-XX:ReservedCodeCacheSize = N(其中N是为特定编译器提供的默认值)主要设置热点代码缓存CodeCache的大小。如果缓存不够,则JIT无法继续编译,并且会去优化,比如编译执行改为解释执行,由此性能会降低。
  • 性能消耗高:编译执行需要较长的预热过程。在 CodeCache 中的代码缓存维护好之前,编译执行相比解释执行需要额外的性能消耗,用来识别热点代码,维护 CodeCache 。同时,编译执行在识别热点代码的过程中,还需要解释执行来帮助提供一些信息支持。

2.2 即时编译器

HotSpot虚拟机中内置了两个即时编译器,分别被称为“客户端编译器”和“服务端编译器”,简称为C1编译器C2编译器

  • C1编译器:进行简单可靠的优化,耗时短,启动快,占用内存小,但优化程度不太高。适合小巧的桌面应用,因此也称为客户端编译器
  • C2编译器:进行激进的优化,执行效率高,但启动慢,占用内存多。比较适合一些资源充裕的服务级应用,因此也称为服务端编译器
  • Graavl编译器:使用 Java 语言进行编写,用来代替用C/C++编写的越来越臃肿的C1、C2编译器,并由此诞生了GraavlVM。

虽然C2的优化效果最好,但是也不可能直接使用C2进行编译,因为使用C2进行编译之前,往往需要通过C1编译器收集很多额外的数据,而C2编译器的最终优化效果,也需要跟C1进行比较才能确定。某些特定情况下,C2的执行效果有可能还不如C1,这时就需要重新优化,重新编译。所以这两个编译器并不是互相取代的关系,而是相互协作的关系。

2.3 分层编译

由于即时编译器编译本地代码需要占用程序运行时间,通常要编译出优化程度越高的代码,所花费的时间便会越长;而且想要编译出优化程度更高的代码,解释器还要收集性能监控信息,这对解释执行阶段的速度也有所影响。为了在程序启动响应速度与运行效率之间达到最佳平衡,HotSpot虚拟机在编译子系统中加入了分层编译的功能,分层编译根据编译器编译、优化的规模与耗时,划分出不同的编译层次。

等级描述性能
0解释代码(程序纯解释执行,并且解释器不开启性能监控功能(Profiling))1
1简单的C1(编译代码使用C1编译器将字节码编译为本地代码来运行,进行简单可靠的稳定优化。不开启性能监控功能)4
2有限的C1(仍然使用C1编译器执行,仅开启方法及回边次数统计等有限的性能监控功能)3
3完整的C1(仍然使用C1编译器执行,开启全部性能监控,除了第2层的统计信息外,还会收集如分支跳转、虚方法调用版本等统计信息)2
4C2编译代码(使用C2编译器将字节码编译为本地代码,相比起C1编译器,C2编译器会启用更多编译耗时更长的优化,还会根据性能监控信息进行一些不可靠的激进优化)5

​JDK8 中提供了参数 -XX:TieredStopAtLevel=<等级> 可以指定使用哪一层编译模型。

3 结语

后端编译是JVM编译运行过程中的关键环节,它将字节码编译成机器码,显著提高了程序的执行效率。后端编译的执行方式包括解释执行、编译执行和混合执行,其中即时编译器(JIT)在运行时将热点代码编译成机器码。C1、C2编译器是HotSpot JVM中的两种主要即时编译器,它们在不同的场景下有不同的应用。分层编译策略则通过在不同层次上应用不同的编译器和优化策略,平衡了启动时间和运行效率。

在后续的博文中,我们将继续深入探讨JVM编译运行的其他方面,包括JIT编译器的内部机制、热点代码的识别和优化策略等。

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!