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

「JVM 编译后话」编译器优化技术

后端编译(即时编译、提前编译)的目标时将字节码翻译成本地机器码,而难点是输出优化质量较高的机器码;

文章目录

      • 1. 优化技术概览
      • 2. 方法内联(Inlining)
      • 3. 逃逸分析(Escape Analysis)
      • 4. 公共子表达式消除(Common Subexpression Elimination)
      • 5. 数组边界检查消除(Array Bounds Checking Elimination)

1. 优化技术概览

PerformanceTacticIndex from OpenJDK Wiki

  • compiler tactics,编译器策略
    • delayed compilation,延迟编译
    • tiered compilation,分层编译
    • on-stack replacement,栈上替换
    • delayed reoptimization,延迟优化
    • program dependence graph representation,程序依赖图表示
    • static single assignment representation,静态单赋值表示
  • speculative (profile-based) techniques,基于性能监控的优化技术
    • optimistic nullness assertions,乐观空值断言
    • optimistic type assertions,乐观类型断言
    • optimistic type strengthening,乐观类型增强
    • optimistic array length strengthening,乐观数组长度增强
    • untaken branch pruning,裁剪未被选择的分支
    • optimistic N-morphic inlining,乐观的多态内联
    • branch frequency prediction,分支频率预测
    • call frequency prediction,调用频率预测
  • proof-based techniques,基于证据的优化技术
    • exact type inference,精确类型推断
    • memory value inference,内存值推断
    • memory value tracking,内存值跟踪
    • constant folding,常量折叠
    • reassociation,重组
    • operator strength reduction,操作符退化
    • null check elimination,空值检查消除
    • type test strength reduction,类型检查退化
    • type test elimination,类型检查消除
    • algebraic simplification,代数简化
    • common subexpression elimination,公共子表达式消除
    • integer range typing
  • flow-sensitive rewrites,数据流敏感重写
    • conditional constant propagation,条件常量传播
    • dominating test detection
    • flow-carried type narrowing,基于流承载的类型缩减转换
    • dead code elimination,无用代码消除
  • language-specific techniques,语言相关的优化技术
    • class hierarchy analysis,类型继承关系分析
    • devirtualization,去虚拟机化
    • symbolic constant propagation,符号常量传播
    • autobox elimination,自动装箱消除
    • escape analysis,逃逸分析
    • lock elision,锁消除
    • lock fusion,所膨胀
    • de-reflection,反射消除
  • memory and placement transformation,内存及代码位置变换
    • expression hoisting,表达式提升
    • expression sinking,表达式下沉
    • redundant store elimination,冗余存储消除
    • adjacent store fusion,相邻存储合并
    • card-mark elimination
    • merge-point splitting,交汇点分离
  • loop transformations,循环变换
    • loop unrolling,循环展开
    • loop peeling,循环剥离
    • safepoint elimination,安全点消除
    • iteration range splitting,迭代范围分离
    • range check elimination,范围查找消除
    • loop vectorization,循环向量化
  • global code shaping,全局代码调整
    • inlining (graph integration),内联
    • global code motion,全局代码外提
    • heat-based code layout,基于热度的代码布局
    • switch balancing,Swith 调整
    • throw inlining
  • control flow graph transformation,控制流图变换
    • local code scheduling,本地代码编排
    • local code bundling,本地代码封包
    • delay slot filling,延迟槽填充
    • graph-coloring register allocation,着色图寄存器分配
    • linear scan register allocation,线性扫描寄存器分配
    • live range splitting
    • copy coalescing,复写聚合
    • constant splitting,常量分裂
    • copy removal,复写移除
    • address mode matching,地址模式匹配
    • instruction peepholing,指令窥孔优化
    • DFA-based code generator,基于正确有限状态机的代码生成

方法内联

// 原始代码
static class B {int value;final int get() {return value;}
}public void foo() {y = b.get();// ...do stuff...z = b.get();sum = y + z;
}// 内联后的代码,即时编译实际效果是作用在代码中间表示或机器码之上的,这里只是以 Java 代码演示效果
public void foo() {y = b.value;// ...do stuff...z = b.value;sum = y + z;
}

内联可以去除方法调用的成本(如查找方法版本、建立栈帧等),可以为其他哟花建立良好条件;一般是优先级最高的优化手段;

冗余访问消除Redundant Loads Elimination

public void foo() {y = b.value;// ...do stuff...z = y;sum = y + z;
}

...do stuff... 不影响 b.value 的情况下,可以将 z=b.value 替换为 z=y,可以不再去访问 b 的局部变量,消除了公共子表达式 b.value(Common Subexpression Elimination);

复写传播Copy Propagation

public void foo() {y = b.value;// ...do stuff...y = y;sum = y + y;
}

变量 z 与变量 y 完全相等,是没有必要使用的,可以使用 y 代替 z;

无用代码消除Dead Code Elimination

public void foo() {y = b.value;// ...do stuff...sum = y + y;
}

上述这些优化带来的代码压缩和执行效率提升在实际机器指令上会表现得更明显;

2. 方法内联(Inlining)

最重要的优化技术之一;把目标方法的代码原封不动的复制到发起调用的方法中(实际 JVM 实现会复杂很多),除了消除方法调用的成本(查找方法版本、创建栈帧等),更重要的是为其他优化建立良好基础;

public static void foo(Object obj) {if (obj != null) {System.out.println("do something");}
}public static void testInline(String[] args) {Object obj = null;foo(obj);
}

示例中全部是Dead Code,但若没有内联,就无法通过无用代码消除来优化掉这些 Dead Code

按照经典编译原理的优化理论,大多数 Java 方法无法进行内联;只有使用 invokespecial 调用私有方法、示例构造器、父类方法,使用 invokestatic 调用静态方法,使用 invokestatic 调用被 final 修饰的方法,才会在编译期进行解析,其他 Java 方法都属于虚方法,都必须在运行时进行方法接收者的多态选择

如 b.get() 直接内联 b.value,若无上下文,将无法确认 b 的实际类型,从而无法内联到对应方法;

无法内联的解法

  • 类型继承关系分析Class Hierarchy Analysis,CHA),在整个应用程序范围做类型分析(确认当前已加载的类中,接口有哪些实现类、类是否存在子类、子类是否复写某个虚方法等);

  • 不是虚方法,直接进行内联;

  • 是虚方法,向 CHA 查询此方法在当前运行时状态下是否有多台选择;若只有一个版本,可假设应用程序的全貌就是只有一个版本,从而进行守护内联Guarded Inlining),此后 VM 可能加载新的类型从而改变 CHA,因此这种内联属于激进预测性优化,必须预备退路(退回解释状态,或重新编译);若存在多态选择,即时编译器将进行最后一次努力,使用内联缓存Inline Cache)来缩减方法调用开销;

  • 内联缓存Inline Cache),建立在目标方法正常入口之前的缓存,在未发生方法调用之前,内联缓存状态为空,当第一次调用发生后,缓存记录下方法接收者的版本信息,以后进来的每次调用比较其版本;若版本一致(单态内联Monomorphic Inline Cache),可通过缓存调用,仅比不内联的非虚方法调用多了一次类型判断开销;若版本不一致,则退化成超多态内联缓存Megamorphic Inline Cache),相当于真正查找虚方法进行方法分派;

方法内联优化与面向对象的编程方式相矛盾,所以在 Java 语言进行方法内联大多数情况下都是激进优化,而类似的激进优化在高性能 JVM 中极为常见,比如激进优化移除出现概率很小的隐式异常、使用概率很小的分支等;但这些优化都必须具备逃生门(重回解释状态);

3. 逃逸分析(Escape Analysis)

最前沿的优化技术之一;分析对象动态作用域,虽不是直接优化代码的手段,却是其他优化措施的依据与前提(类似继承关系分析);

  • 方法逃逸,一个在方法里定义的对象可能被外部方法引用(作为调用参数传递给其他方法);
  • 线程逃逸,一个对象被跨线程访问(赋值给可能被其他线程访问的实例变量);

逃逸分析的优化用途

  • 栈上分配Stack Allocations

JVM 对堆中分配的对象进行 GC 需要大量计算标记筛选出可回收对象,并进行回收和整理;若一个对象不会发生线程逃逸(实际这类对象的比例很大),这个对象就可能在栈上分配内存,对象可以随方法的结束自动销毁,GC 的压力将大幅下降;

  • 标量替换Scalar Replacement

将一个 Java 对象拆解成若干个用原始类型表示的成员变量;若一个对象不会发生方法逃逸,这个对象就可以被拆散为标量,程序执行就不用创建这个对象,而是直接在栈上创建成员变量(栈上分配,大几率分配到物理机器的高速寄存器中存储),这还可以作为进一步优化的基础; - 标量,无法再分解成更小数据表示的数据,如 JVM 的原始数据类型 int,long 等,以及 reference 类型; - 聚合量Aggregate),一个可以继续分解的数据,如 Java 中的对象;

  • 同步消除Synchronization Elimination),若一个对象不会发生线程逃逸,那么对这个变量的读写同步操作可以被安全的消除掉;

逃逸分析的计算成本(如复杂的数据流敏感的过程间分析)非常高,无法保证逃逸分析带来的性能收益高于它的消耗,JDK 6 才开始支持逃逸分析(Update 23 服务端编译器默认开启);

inline 关键字定义 Java 内联类型可以实现 C# 中值类型相对应的功能,可以令逃逸分析变得简单很多;

逃逸分析模拟演示

// 原始代码
public int test(int x) {int xx = x + 2;Point p = new Point(xx, 42);return p.getX();
}
// 步骤1: 构造函数内联
public int test(int x) {int xx = x + 2;Point p = point_memory_alloc();     // 在堆中分配 P 对象的示意方法p.x = xx;                           // Point 构造函数被内联p.y = 42return p.x;                         // Point::getX()被内联
}
// 步骤2: 标量替换
public int test(int x) {int xx = x + 2;int px = xx;                        // p.x 与 p.y 不会发生方法逃逸,可以直接替换为标量 px、pyint py = 42;return px;
}
// 步骤3: 无效代码消除
public int test(int x) {return x + 2;                       // py 对运行效果无影响,为 Dead Code,可消除
}

逃逸分析相关参数

-XX:+DoEscapeAnalysis,手动开启逃逸分析;
-XX:+PrintEscapeAnalysis,查看分析结果;
-XX:+EliminateAllocations,开启标量替换;
-XX:+EliminateLocks,开启同步消除;
-XX:+PrintEliminateAllocations,查看标量的替换情况;

大型程序中实施逃逸分析可能出现效果不稳定的情况,或分析过程耗时却无法有效判别出非逃逸对象,导致性能(即时编译的收益)下降;

4. 公共子表达式消除(Common Subexpression Elimination)

语言无关的经典优化技术之一;对于公共子表达式,没必要重复计算,可以直接用前面计算的结果替换;

  • 公共子表达式,如果一个子表达式已经被计算过,且表达式中变量的值不曾发生变化,那这个子表达式就可以当做公共子表达式;基本代码块中的为局部公共子表达式Local Common Subexpression Elimination),跨基本代码块则可称为全局公共子表达式Global Common Subexpression Elimination);

公共子表达式消除演示

// 原始代码
int d = (c * b) * 12 + a + (a + b * c);

javac 直译的 Class 格式效果

iload_2     // b
imul        // 计算b*c
bipush 12   // 推入12
imul        //计算(c*b)*12
iload_1     //a
iadd        //计算(c*b)*12+a
iload_1     //a
iload_2     //b
iload_3     //c
imul        //计算b*c
iadd        //计算a+b*c
iadd        //计算(c*b)*12+a+a+b*c
istore 4

即时编译优化效果

// 将 c*b 和 b*c 用 E 表示,消除公共子表达式
int d = E * 12 + a + (a + E);// 代数简化(Algebraic Simplification)
int d = E * 13 + a + a;

5. 数组边界检查消除(Array Bounds Checking Elimination)

语言相关的经典优化技术之一;Java 语言作为一门动态安全的语言,会自动对数组的读写访问索引合法性做检查,当超出地址范围,则抛出 java.lang.ArrayIndexOutOfBoundsException,这对软件开发很友好,但对 JVM 却是一个性能负担;若能在编译期根据数据流分析判定索引一直在数组边界内,就可以消除数组上下边界的检测,从而节省很多次条件判断操作;

类似的消除还可以发生在空指针检查(NullPointException)、除数为零检查(ArithmeticException)、自动装箱消除Autobox Elimination)、安全点消除Safepoint Elimination)、消除反射Dereflection)等;针对这些检查的消除方式,还可以采用隐式异常处理的思路;

隐式异常处理示例

// 原始伪代码
if(foo != null) {return foo.value;
} else {throw new NullPointException();
}// 隐式异常消除后的伪代码
try {return foo.value;
} catch (segment_fault) {uncommon_trap();
}

JVM 注册一个 Segment Fault 信号的异常处理器(uncommon_trap(),针对进程层面的异常处理器,与 try-catch 的线程级异常处理器不同),当 foo 不为空,可以省去判空的开销;但若 foo 真为空,会转到异常处理器(涉及进程从用户态转内核态,结束后再转用户态)恢复中断并抛出 NullPointException,这将远比一次判空要慢;借助 VM 在运行期收集的性能监控信息,判定 foo 极少为空时,采用这样的优化方式会更值得;


上一篇:「JVM 编译优化」提前编译器

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》

相关文章:

「JVM 编译后话」编译器优化技术

后端编译(即时编译、提前编译)的目标时将字节码翻译成本地机器码,而难点是输出优化质量较高的机器码; 文章目录1. 优化技术概览2. 方法内联(Inlining)3. 逃逸分析(Escape Analysis)4…...

【python学习笔记】:输出与输入

01 输出方式 表达式语句、print()函数和使用文件对象的write()方法。 02 输出形式 格式化输出str.format()函数、转成字符串可以使用repr()或str()函数来实现。 (1)repr():产生一个解释器易读的表达形式,便于字符串的拼接。 例:输出平方与…...

汽车电子社区交流宣传

http://t.csdn.cn/VSLO0http://t.csdn.cn/VSLO0 当今的汽车行业已经进入了数字化时代,汽车电子软件的开发变得越来越重要。在这个领域,开发者们需要应对各种挑战,包括复杂的硬件和软件交互、高效的嵌入式编程和安全性要求。为了帮助汽车电子…...

String、StringBuilder 和 StringBuffer 详解

碎碎念 这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C、Visual Basic、C# 等。字符串使用 String 来表示,字符串一旦被创建出来就不会被修改,当你想修改StringBuffer 或者是 …...

windows服务器上传文件解决方案

1.说明 1.如果上传到linux系统,通常使用ftp相关技术,配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式(常规是开启windows的ftp服务&#xff0…...

Android Studio翻译插件推介(Translation)

前言 Android Studio翻译插件适合英语水平不太好的程序员(比如:我),最常用的翻译插件Translation和AndroidLocalize,本文主要讲解Translation,亲测可用。 先看看效果:这里是Android的API,任意选…...

DNS,DNS污染劫持,DNS加密

1. DNS(Domain Name System)DNS(Domain Name System), 也叫网域名称系统,是互联网的一项服务。它实质上是一个 域名 和 IP 相互映射的分布式数据库.DNS(Domain Name Server,域名服务…...

【Python】如何度量优秀代码——静态分析工具

静态分析工具背景有哪些静态分析工具呢度量Python代码的静态属性度量Python的生态系统代码的坏味道在类层面上在方法层面上结语背景 静态代码分析工具能够提炼出丰富的代码静态属性信息,这使得程序员可以对代码的复杂性、可修改性和可读性有进一步的了解。 有哪些…...

Open3D 点云高程归一化(基于2维地面点,Python版本)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前的博客中Open3D 点云高程归一化(基于地面点,Python版本)是基于三维空间进行最近地面点的查询操作,这里对其进行修改一下,将点云投影到水平面,基于二维空间进行最近地面点的查询,这种方式对一些较为陡峭的…...

动态系统的建模与分析

前言 CS小菜鸡控制理论入门 视频学习笔记 视频传送门:动态系统的建模与分析】9_一阶系统的频率响应_低通滤波器_Matlab/Simulink分析 拉普拉斯变换 F(s)L{f(t)}∫0∞f(t)e−stdtF(s)\mathcal{L}\{f(t)\}\int_0^\infty f(t)e^{-st}\mathrm{d}tF(s)L{f(t)}∫0∞​f(t)…...

QCC51XX---HCI log

高通在新的S3/S5以及往后新的平台上面,引入了一个新的调试功能。就是标题说的HCI log,他类似air trace那样用来分析蓝牙协议的,这样我们就可以很详细地找到通信协议之间哪个部分出了问题。以前我们都是通过抓包器抓air trace分析的,抓包器一个要几十万,学会这个功能就相当…...

Redis四 原理篇

《Redis四 原理篇》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《Redis四 原理篇》《Redis四 原理篇》1、原理篇-Redis数据结构1.1 Redis数据结构-动态字符串1.2 Redis数据结构-intset1.3 Redis数据结构-Dict1.4 Redis数据结构-ZipList1.4.1 Redis数据…...

从0开始写Vue项目-Vue实现数据渲染和数据的增删改查

从0开始写Vue项目-环境和项目搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue2集成Element-ui和后台主体框架搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue页面主体布局和登录、注册页面_慕言要努力的博客-CSDN博客从0开始写Vue项目-SpringBoot整合Mybatis-plus实现…...

AI技术的发展,人工智能对我们的生活有那些影响?

人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。 人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一…...

Unity中的Mathf数学运算讲解(值得收藏)

Unity中的Mathf数学运算有哪些? Mathf.Abs(f)绝对值 计算并返回指定参数 f 绝对值 例如: // 输出 10 Debug.log(Mathf.Abs(-10)) Debug.log(Mathf.Abs(10))Mathf.Sin正弦 static function Sin (f : float) : float 计算并返回以弧度为单位指定的角 f 的…...

ABBYY FineReader16最新PDF图片文字识别软件

ABBYY FineReader16是非常好的一款 OCR 识别软件(可以识别不可编辑的PDF和图片文件),操作非常简单。ABBYY FineReader 16是一款知名的OCR文字识别软件(图片文字识别)。ABBYY 16采用了ABBYY最新推出的基于AI的OCR技术&a…...

Leetcode14. 最长公共前缀

一、题目描述: 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 “”。 示例 1: 输入:strs [“flower”,“flow”,“flight”] 输出:“fl” 示例 2: 输入:…...

JTT808jt1078

前言 交通部与2016年10月份推出了JT/T 1078-2016标准&#xff0c;全称是<道路运输车辆卫星定位系统视频通信协议> JTT808 808消息头内容如下表所示&#xff1a; 起始字节字段数据类型描述及要求0消息IDWORD2消息体属性WORD消息体属性格式结构图见图24终端手机号BCD[6…...

数字孪生加持,水利水电工程或将实现全生命周期管理

水利水电工程在数字孪生技术的加持&#xff0c;使得建设和运营更加高效和智能化&#xff0c;将工程中各种元素、过程和系统数字化&#xff0c;并建立数字孪生模型&#xff0c;以实现工程建设和运营的智能化管理。数字孪生对水利水电实现对工程建设的全生命周期管理&#xff0c;…...

RA4M2开发(3)----读取ISL29035数据,并在OLED上显示,串口打印

概述 HS3003是一种数字式温湿度传感器&#xff0c;可以测量环境中的温度和湿度。读取HS3003的数据需要连接传感器到一个数据采集系统&#xff0c;一般是微处理器或者单片机。以下是一个简单的读取HS3003数据的概述&#xff1a; 连接电路&#xff1a;将HS3003传感器连接到微处…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

什么是VR全景技术

VR全景技术&#xff0c;全称为虚拟现实全景技术&#xff0c;是通过计算机图像模拟生成三维空间中的虚拟世界&#xff0c;使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验&#xff0c;结合图文、3D、音视频等多媒体元素…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…...