Java后端编译与优化

 如果我们将字节码看作是程序语言的一种中间表示形式,那编译器无论在何时、在何种状态下把Class文件转换成与本地基础设施相关的二进制机器码,它都可以视为整个编译过程的后端。

1 即时编译器

即时编译器是指运行时将热点代码编译成本地机器码,并以各种手段尽可能地进行代码优化的后端编译器。

1.1 解释器与编译器

1.1.1 解释器

将字节码解释成具体平台的机器码。

其主要作用是:

1)当程序需要迅速启动或执行时,解释器可以首先发挥作用,省去编译的时间。

2)节约内存。

3)作为编译器激进优化时后备的逃生门。

1.1.2 编译器

程序启动后,随着时间推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,减少解释器的中间损耗,获得更高的执行效率。

HotSpot虚拟机中内置两个(或三个)即时编译器,其中两个分别为“客户端编译器”(Client Compiler)简称为C1编译器和“服务端编译器”(Server Compiler)简称为C2编译器,第三个是JDK10才出现的,长期目标是代替C2点Graal编译器。

1.1.3 分层编译

为了在程序启动响应速度和运行效率之间达到最佳平衡,HotSpot虚拟机在编译子系统中加入了分层编译的功能。在JDK 7 服务端模式虚拟机中作为默认编译策略被开启。

第0层。程序纯解释执行,并且解释器不开启性能监控功能。

第1层。使用客户端编译器将字节码编译为本地代码来运行,进行简单可靠的稳定优化,不开启性能监控功能。

第2层。仍使用客户端编译器执行,仅开启方法及回边次数统计等有限的性能监控功能。

第3层。仍使用客户端编译器执行,开启全部性能监控,除了第2层的统计信息外,还会收集如分支跳转、虚方法调用版本等全部等统计信息。

第4层。使用服务端编译器将字节码编译为本地代码,启用更多耗时更长的优化,还会根据性能监控信息进行一些不可靠的激进优化。

1.2 编译对象与触发条件

1.2.1 编译对象

会被即时编译器编译的目标是“热点代码”,主要由两类:1)被多次调用的方法。2)被多次执行的循环体。

1.2.2 触发条件

判断某段代码是不是热点代码,是否触发即时编译的这种行为称为“热点探测”。进行热点探测并不一定要知道方法具体被调用了多少次,目前主流的热点探测判定方式有两种:

1)基于采样的热点探测。虚拟机会周期性地检查各个线程的调用栈顶,如果发现某些方法经常出现在栈顶,那这个方法就是“热点方法”。

该方法实现简单高效,还可以很容易地获取方法调用关系,缺点是很难精确地确认一个方法的热度,容易受到线程阻塞的影响。

2)基于计数器的热点探测。为每个方法(甚至是代码块)建立计数器,统计方法的执行次数。

该方法实现起来要麻烦些,但是统计结果相对来说更加精确严谨。

       方法调用计数器并不是方法被调用的绝对数,而是一个相对的执行频率。当超过一定时间限度,如果次数仍不足,那该方法的调用计数器就会被减少一半。这个过程被称为计数器热度的衰减,而这段时间被称为半衰周期。

1.3 编译过程

在默认条件下,虚拟机在编译器还未完成编译之前,都仍然将按照解释器方式继续执行代码,而编译动作则在后台的编译线程中进行。

服务端编译器和客户端编译器的编译过程是有所差别的。

1.3.1 客户端编译器

是一个相对简单快速的三段式编译器,主要的关注点在于局部性的优化,而放弃了许多耗时较长的全局优化手段。

图 客户端编译器大致的执行过程

第一阶段:将字节码完成一部分基础优化,如方法内联、常量传播等。然后构造成一种高级中间代码表示(HIR,即与目标机器指令集无关的中间表示)。HIR使用静态单分配(SSA)的形式来代表代码值。

第二阶段:在HIR上完成另一些优化,如空值检查消除、范围检查消除等。然后产生低级中间代码表示(LIR,即与目标机器指令集相关的中间表示)。

最后阶段:使用线性扫描算法在LIR上分配寄存器,并在LIR上做窥孔优化,然后产生机器代码。

1.3.2 服务端编译器

是专门面向服务端的典型应用场景,并为服务器性能配置针对性调整过的编译器,也是一个能容忍很高优化复杂度的高级编译器。

会执行大部份经典优化动作,还可能根据解释器或客户端编译器提供的性能监控信息,进行一些不稳定的预测性激进优化。

2 编译器优化技术

编译器的工作,难道不在于能否成功翻译出机器码,而在于输出代码优化质量的高低。

2.1 方法内联

最重要的优化技术之一。被称为优化之母,除了消除方法调用的成本之外,它更重要的意义是为其他优化手段建立良好的基础。

方法内联是把目标方法的代码原封不动地“复制”到发起调用到方法中。

public class MethodInline {
    static void method1() {
        System.out.println("method1");
    }

    static void method2() {
        System.out.println("method2");
        method1();
    }
}

上面代码经过方法内联后变为:

static void method2() {
    System.out.println("method2");
    System.out.println("method1");
}

在实际的内联过程,因为Java虚方法都必须在运行时进行方法接收者都多态选择,它们都可能存在多于一个版本的方法接收者。

2.1.1 类型继承关系分析(CHA)

为了解决虚方法的内联问题,Java虚拟机首先引入类型继承关系分析技术,范围为整个应用程序。用于确定目前已加载的类中,某个接口是否有多于一种的实现、某个类是否存在子类、某个子类是否覆盖了父类的某个虚方法等信息。

图 Java虚方法内联

2.2 逃逸分析

最前沿的优化技术之一。并不是直接优化代码的手段,而是为其他优化措施提供依据的分析技术。

基本原理:分析对象动态作用域,当一个对象在方法里被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种称为方法逃逸;甚至可能被外部线程访问到,称为线程逃逸。

如果证明一个对象不会逃逸到方法或线程之外,则可能为这个对象实例采取不同程度的优化,如:

  1. 栈上分配。如果确定一个对象不会逃逸出线程之外,那可以让这个对象在栈上分配内存(平常在堆中存储对象数据)。对象所占用到内存空间可以随栈帧出栈而销毁。栈上分配支持方法逃逸而不支持线程逃逸。
  2. 标量替换。一个数据已经无法再分解成更小的数据来表示了,称为标量,相反则称为聚合量。如果一个对象不会被方法外部访问,并且这个对象可被拆散,那么程序真正执行的时候将可以不去创建这个对象,而改用直接创建它的若干个被这个方法使用的成员变量来代替。
  3. 同步消除。线程同步本身是一个相对耗时的过程,如果一个变量不会逃逸出线程,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施就可以安全地消除掉。

 2.3 公共子表达式消除

语言无关的经典优化技术之一。含义是:如果一个表达式E之前已经被计算过,并且从先前计算到现在E中所有变量到值都没发生变化,那么E被称为公共子表达式。

int d = (c * b) * 12 + a + b * c。

优化过程为:

c * b 与 b * c是等价的,且这这段过程中,c与b的值没有发生变化。所以c * b 为公共表达式,设c * b = E,则经过优化的代码为:

int = E * 12 + a + E。

2.4 数组边界检查消除

语言相关的经典优化技术之一。Java每次数组元素的读写都带有隐含的条件判断操作(如检查下标是否合法)。

虚拟机会注册一个Segment Fault信号的异常处理器,这样,当下标合法时,对数组的访问不会有任何额外的开销,当不合法时,必须转到异常处理器中恢复中断并抛出NullPointException异常。此时速度远比上面的隐含条件判断操作慢得多。

所以假如下标经常不合法,这样的优化反而会让程序更慢。HotSpot会根据运行期收集到的性能监控信息自动选择合适的方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15978.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx反向代理 404 问题

发现我们设置了反向代理没有起作用,最后发现原来是伪静态惹得祸 解决nginx添加反向代理代码不生效-与原rewrite伪静态规则冲突了 以thinkphp官方给的伪静态为例 if (!-e $request_filename){rewrite ^(.*)$ /index.php?s$1 last; break;}仔细研究发现发现问…

第48步 深度学习图像识别:RegNet建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 (1)RegNet RegNet (Regulated Networks) 是一种由 Facebook AI 的研究者们在 2020 年提出的神经网络架构,旨在探索网络架构设计的各种可能性,并找出最优的网络设计规则。RegNet 的核心理…

全志V3S嵌入式驱动开发(四种启动方式)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 对于v3s的启动方式分析,怎么研究都不为过。对我们整个系列文章比较熟悉的同学来说,这幅图真的是太熟悉了, 整个流程不复杂。它主要是告诉我们,v3s加载的顺序是怎么样的…

基于智能手机的医院服务客户端设计与实现(论文+源码)_kaic

摘 要 近年来,随着中国经济的迅猛发展,医疗技术水平也在不断提高,但由于人口数目巨大,导致医疗资源人均分配不足的情况依旧十分严峻。预约挂号一直是制约医疗机构服务质量提高的主要环节之一。在传统预约挂号方式下,繁…

APP测试面试题快问快答(六)

26. App测试工程师到底测些什么? 考察点:是否有移动app测试的相关经验 界面测试 功能测试 兼容性测试 性能测试 稳定性测试 安全性测试 环境的测试 专项测试 –安装,卸载,流量,电量,弱网和FPS 27…

华为云函数工作流FunctionGraph新手操作指南

函数工作流(FunctionGraph)是华为云提供的一款无服务器(Serverless)计算服务,无服务器计算是一种托管服务,服务提供商会实时为你分配充足的资源,而不需要预留专用的服务器或容量,真正…

【LeetCode】217. 存在重复元素

217. 存在重复元素(简单) 方法一:哈希表长度比较 思路 针对重复元素,很容易就想到 set。我们可以先将 nums 中的所有元素存入set,然后比较两个数据结构的长度,如果相等则说明不存在重复元素,反…

【计算机视觉 | 图像分类】arxiv 计算机视觉关于图像分类的学术速递(6月 29 日论文合集)

文章目录 一、分类|识别相关(12篇)1.1 Pseudo-Bag Mixup Augmentation for Multiple Instance Learning Based Whole Slide Image Classification1.2 Improving Primate Sounds Classification using Binary Presorting for Deep Learning1.3 Challenges of Zero-Shot Recognit…

pycharm如何给一串中文快捷加引号(方法二)

点击上方“Python爬虫与数据挖掘”,进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 商人重利轻别离,前月浮梁买茶去。 大家好,我是皮皮。 一、前言 前几天在Python白银群【此类生物】问了一个Pycharm基础的问题&a…

EcoVadis 2023年最新评分细则

【EcoVadis 2023年最新评分细则】 Ecovadis 的四大主题 EcoVadis 企业社会责任评级方法的目标是通过其方针政策、实施执行和绩效反馈来衡量一家公司的企业社会责任管理系统的质量。 EcoVadis企业社会责任(CSR)评估方法基于七项基本原则(如图&…

架构-新教材补充内容

系统工程 两分,需要计算的选择题 #mermaid-svg-UYHr1rzu8HIFQAsT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-UYHr1rzu8HIFQAsT .error-icon{fill:#552222;}#mermaid-svg-UYHr1rzu8HIFQAsT .error-te…

SQL多表查询

多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。 前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,…