「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服务࿰…...
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标准,全称是<道路运输车辆卫星定位系统视频通信协议> JTT808 808消息头内容如下表所示: 起始字节字段数据类型描述及要求0消息IDWORD2消息体属性WORD消息体属性格式结构图见图24终端手机号BCD[6…...
数字孪生加持,水利水电工程或将实现全生命周期管理
水利水电工程在数字孪生技术的加持,使得建设和运营更加高效和智能化,将工程中各种元素、过程和系统数字化,并建立数字孪生模型,以实现工程建设和运营的智能化管理。数字孪生对水利水电实现对工程建设的全生命周期管理,…...
RA4M2开发(3)----读取ISL29035数据,并在OLED上显示,串口打印
概述 HS3003是一种数字式温湿度传感器,可以测量环境中的温度和湿度。读取HS3003的数据需要连接传感器到一个数据采集系统,一般是微处理器或者单片机。以下是一个简单的读取HS3003数据的概述: 连接电路:将HS3003传感器连接到微处…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...
