当前位置: 首页 > news >正文

Java 的即时编译器(JIT)优化编译探测技术

我们来深入解析 Java 的即时编译器(JIT)优化。这是 Java 能从“解释执行”的慢语言演变为高性能语言的关键所在。

一、核心概念:解释执行 vs. 编译执行

Java 程序最初是通过 解释器 (Interpreter) 来执行的。解释器逐条读取字节码,逐条翻译成机器码执行。优点是启动快,但缺点是执行慢。

即时编译 (Just-In-Time Compilation, JIT) 是为了解决解释器效率低下而诞生的技术。它的核心思想是:将频繁执行的代码(称为“热点代码”)编译成本地机器码,并进行了深度优化,然后缓存起来。下次执行时直接运行机器码,效率得到极大提升。


二、分层编译 (Tiered Compilation)

现代 JVM(如 HotSpot)采用了一种分层编译的策略,结合了不同编译器的优点,以实现启动速度和峰值性能的最佳平衡。它包含了多个编译层级:

层级描述目的编译器
第0层:解释执行所有代码最初都由此开始。最快的启动速度解释器
第1层:C1 简单编译开启少量优化(如方法内联的基础版),不收集性能监控数据。以最小代价提升执行速度C1 (Client Compiler)
第2层:C1 受限编译开启更多优化,收集简单的性能监控数据(如方法调用次数、循环回边次数)。为C2编译收集数据,同时提供更好的性能C1
第3层:C1 完全编译开启所有优化,收集所有性能监控数据。提供良好的性能,并收集完整数据供C2使用C1
第4层:C2 编译使用第2层和第3层收集的数据,进行激进的全方位优化提供最高的峰值性能C2 (Server Compiler)

工作流程如下图所示,代码会沿着解释器 -> C1 -> C2 的路径逐步优化:

图表

C1 与 C2 编译器的区别

特性C1 编译器 (Client Compiler)C2 编译器 (Server Compiler)
编译速度
优化程度,简单可靠的优化,激进的全方位优化
性能分析收集少量数据依赖 C1 收集的丰富数据
目标优化启动速度,适用于 GUI 客户端优化峰值性能,适用于服务器端

现代默认策略:在 JDK 8 及以后,64位服务端模式下,分层编译默认是开启的-XX:+TieredCompilation)。这种策略使得程序在启动阶段能快速通过C1编译获得性能提升,随后在后台利用C2编译进行极致优化,完美兼顾了启动速度和长期运行的性能。


三、热点代码探测 (Hot Spot Code Detection)

JIT 并不会编译所有代码,只会编译那些“热点”代码。如何判定一段代码是不是热点呢?

HotSpot 虚拟机采用了基于计数器的热点探测。它为每个方法甚至每个代码块建立计数器,统计执行次数。

  1. 方法调用计数器 (Invocation Counter)

    • 统计方法被调用的次数。

    • 默认阈值在 Client 模式下是 1500 次,在 Server 模式下是 10000 次(-XX:CompileThreshold)。

    • 超过这个阈值,就会触发 JIT 编译。

  2. 回边计数器 (Back Edge Counter)

    • 统计循环体的循环次数(“回边”指字节码中跳回循环起始位置的指令)。

    • 它的存在是为了识别循环密集的热点代码。

    • 触发编译的阈值由 -XX:OnStackReplacePercentage 等参数间接控制。

热度衰减 (Counter Decay):如果一段时间内方法的调用次数仍未达到阈值,方法的计数器值会减少一半。这个过程称为方法调用的热度衰减。这确保了“热点”是那些持续被调用的代码,而非只是曾经被频繁调用过的代码。


四、核心优化技术

JIT 编译器在编译热点代码时,会进行大量优化。以下是几个最关键的技术:

1. 方法内联 (Method Inlining)

这是最重要、最基础的优化。它将被调用方法的代码直接“复制”到调用者方法中,从而避免了一次真实的方法调用(创建新的栈帧、参数传递、跳转等)。

  • 优点

    • 消除了方法调用的开销

    • 为其他优化创造了条件。因为代码在一个方法内了,编译器可以在此基础上做更多的优化,如死代码消除

  • 例子

    java

    // 内联前
    int a = Math.max(10, 20); // 需要调用方法// 内联后(效果等同于)
    int a = 10 > 20 ? 10 : 20; // 直接使用逻辑,无方法调用
  • 限制:主要由方法体积决定。默认情况下,小于 35 字节(-XX:MaxInlineSize)的方法会被直接内联,更大的方法则可能根据其“热度”和“成本”来决定是否内联。

2. 逃逸分析 (Escape Analysis)

这是 JIT 编译器进行其他高级优化的基础。它通过分析对象的作用域,来判断一个对象是否会在方法之外被访问到(即是否“逃逸”)。

  • 三种逃逸状态

    1. 不逃逸 (NoEscape):对象仅在当前方法中使用。

    2. 方法逃逸 (ArgEscape):对象作为参数传递给其他方法。

    3. 线程逃逸 (GlobalEscape):对象被赋值给类变量或可以被其他线程访问到(如赋值给 static 字段)。

基于逃逸分析,JIT 可以做以下三种优化

  • 栈上分配 (Stack Allocation)

    • 如果确定一个对象不会逃逸出方法,那么这个对象就可以直接在栈上分配内存,而不是在堆上。

    • 好处:对象随栈帧出栈而自动销毁,极大减轻了 GC 的压力

  • 标量替换 (Scalar Replacement)

    • “标量”是指无法再分解的数据,如基本数据类型(intlong 等)。

    • 如果一個对象不会逃逸,并且可以被进一步分解,那么 JIT 不会创建这个对象,而是直接创建它的成员变量。

    • 例子

      java

      // 原始代码
      Point point = new Point(10, 20);
      System.out.println(point.x + point.y);// 经过标量替换后(效果等同于)
      int x = 10, y = 20; // 直接在栈上分配两个int,没有创建Point对象
      System.out.println(x + y);

  • 同步消除 (Lock Elision)

    • 如果一个对象被确定不会线程逃逸,即不会被其他线程访问到,那么对这个对象施加的同步措施(如 synchronized)就可以被安全地消除掉。

注意:逃逸分析本身是一个相对耗时的过程,但它的收益(尤其是标量替换和同步消除)是巨大的。


总结与启示

技术核心思想带来的好处
分层编译C1 保启动,C2 保性能,分工协作兼顾应用的启动速度峰值性能
热点探测谁热我就优化谁将有限的编译资源用在刀刃上,效率最大化
方法内联用空间换时间,消除方法调用消除了调用开销,是后续更多优化的基础
逃逸分析分析对象作用域栈上分配(减GC压力)、标量替换(减内存占用)、同步消除(提并发性能)

对开发者的启示

  1. 不要过早优化:JIT 已经非常智能,很多我们手动的“优化”(如循环展开)可能编译器做得更好。

  2. 编写“JIT友好”的代码

    • 保持方法小巧:有助于方法内联。

    • 尽量限制变量的作用域(如使用局部变量而非成员变量):有助于逃逸分析进行优化。

    • 避免不必要的同步:万一逃逸分析失败,同步就会成为真正的性能开销。

  3. 理解原理:理解这些优化原理,有助于我们诊断性能问题。例如,通过 -XX:+PrintCompilation 查看哪些方法被编译了,通过 -XX:+PrintInlining 查看内联情况。


文章转载自:

http://MSCvkbn6.qbgdy.cn
http://7KCaDTRu.qbgdy.cn
http://abL0wrLl.qbgdy.cn
http://2MHBxbe3.qbgdy.cn
http://pZYMgCtP.qbgdy.cn
http://VRFyVnA9.qbgdy.cn
http://r2Afezbz.qbgdy.cn
http://dG14iHwB.qbgdy.cn
http://C3tamTYf.qbgdy.cn
http://rcBmGe2j.qbgdy.cn
http://WBTMUJvJ.qbgdy.cn
http://gWguN33i.qbgdy.cn
http://mGHkUpii.qbgdy.cn
http://jUhOr3Ac.qbgdy.cn
http://bdpqLya0.qbgdy.cn
http://VhE2wr5t.qbgdy.cn
http://4hS54Hlu.qbgdy.cn
http://lvzQDElu.qbgdy.cn
http://Ndbb0NgO.qbgdy.cn
http://Lf7sgdf0.qbgdy.cn
http://T9fWMCGY.qbgdy.cn
http://DHV03o3u.qbgdy.cn
http://44vQpgQI.qbgdy.cn
http://EA4atiJo.qbgdy.cn
http://4fzLvNqg.qbgdy.cn
http://0GNzoRGV.qbgdy.cn
http://BxBm6io1.qbgdy.cn
http://JMualsBm.qbgdy.cn
http://l1TX69PR.qbgdy.cn
http://nCu64L8S.qbgdy.cn
http://www.dtcms.com/a/380980.html

相关文章:

  • 《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(4)
  • 综合体EMS微电网能效管理系统解决方案
  • ARM2.(汇编语言)
  • 从“插件化“到“智能化“:解密Semantic Kernel中Microsoft Graph的架构设计艺术
  • TDengine 特殊函数 MODE() 用户手册
  • 导购类电商平台的安全架构设计:防刷单与反作弊系统实现
  • 阿里云可观测 2025 年 8 月产品动态
  • 阿里云监控使用
  • 九识智能与北控北斗合作研发的L4级燃气超微量高精准泄漏检测无人车闪耀服贸会,守护城市安全
  • vulhub漏洞复现-redis-4-unacc (redis未授权访问)
  • 数据库分库分表是考虑ShardingSphere 还是Mycat?
  • CSP认证练习题目推荐 (3)
  • R geo 然后读取数据的时候 make.names(vnames, unique = TRUE): invalid multibyte string 9
  • Linux:线程封装
  • 电动指甲刀技术方案概述
  • 机器人巡检与巡逻的区别进行详细讲解和对比
  • 程序内存中堆(Heap)和栈(Stack)的区别
  • 提高软件可靠性的思路
  • (1-10-2)MyBatis 进阶篇
  • ZedGraph库里实现坐标拖动图形的背景显示
  • SpringBoot应用开发指南:从入门到高级配置与自动装配原理
  • 怎么快速规划好旅行
  • 一带一路经济走廊及其途经城市图件
  • k8s的设计哲学
  • 城市污水管网流量监测方法
  • 计算机视觉进阶教学之特征检测
  • 基于OpenVinoSharp和PP-Vehicle的车辆检测
  • [论文阅读] 人工智能 | 软件工程 - 软件测试 | 从黑盒到透明:AUTOSTUB用进化算法打通符号执行的“最后一公里”
  • zmq源码分析之io_thread_t
  • 什么是财报自动识别?5分钟OCR识别录入1份财务报表