《深入理解JAVA虚拟机(第2版)》- 第11章 - 学习笔记

news2024/9/25 23:22:39

第11章 晚期(运行期)优化

11.1 概述

  1. 频繁执行的方法或代码块,被认定为“热点代码”(Hot Spot Code)。
  2. 为了提高热点代码的执行效率,在运行期,将热点代码编译成本地机器码并进行优化,完成这个功能的编译器叫做即时编译器
  3. 对于虚拟机来说即时编译器并不是必须的,Java虚拟机规范当中并没有规定必须有即时编译器,更没有规定如何去实现它。

11.2 HotSpot虚拟机内的编译器

11.2.1 解释器与编译器
  1. 主流虚拟机(例如:HotSpot、J9等)都是采用解析器和编译器并存的架构。
    • 解释器:优点是能够快速启动和执行,不需要编译,不需要很大的内存空间。缺点是执行效率不高。
    • 编译器:缺点是需要将代码编译成本地代码,因此对内存空间有一定要求。优点是执行效率高。
    • 解释器通常作为编译器激进优化失败后的“逃生门”,即退回到解释器执行。
    • 解析器与编译器的交互,如下图: 在这里插入图片描述
  2. HotSpot虚拟机内置了两个编译器:Client Complier(即C1编译器)和Server Complier(即C2编译器)。默认情况下,HotSpot采用的是解释器和其中一个编译器来配合工作的。至于采用的是哪个编译器,是由虚拟机的运行模式(Client模式或Server模式)来决定的。
  3. 为了使程序启动速度和执行效率达到一个平衡,HotSpot虚拟机还启用了分层编译的策略,基于编译器编译、优化的规模和耗时,划分了以下三个层次的编译。
    • 第0层:解释执行,不开启性能监控功能,可触发第1层编译。
    • 第1层:也就是C1编译,将字节码编译成本地代码,并进行简单、可靠的优化,根据具体的情况去判断是否开启性能监控功能。
    • 第2层:也就是C2编译,和第一层一样也是将字节码编译成本地代码,不同的是会开启编译耗时较长的优化,甚至会根据性能监控信息进行一些不那么可靠的激进优化。
11.2.2 编译的对象与触发条件
  1. 编译的对象就是我们所说的“热点代码”,热点代码又分为两类:

    • 多次调用的方法:以方法为单位进行编译,这种编译是虚拟机中的标准JIT编译

    • 多次执行的代码块:还是以方法为单位进行编译,这种编译是发生在代码执行的过程中,所以称为栈上替换(On Stack Replacement,OSR,即栈帧还在栈上,方法就被替换了)

  2. 编译的触发条件
    上边提到的多次调用、多次执行,这里的“多次”具体到底是多少次?虚拟机是如何统计出来方法或代码块执行次数的?回答了这两个问题,也就说明了编译的触发条件。

    判断一段代码是不是热点代码,是否能触发即时编译,这样的行为称为热点探测(Hot Spot Detection)。

    目前,主要的热点探测判定方式有两种,如下:

    • 基于采样的热点探测:在一个周期内观察所有线程的栈顶,某个方法在栈顶出现的频次较多则为热点代码。这种方式实现简单,但是由于线程阻塞等原因,导致结果不准确

    • 基于计数器的热点探测:每个方法(或代码块)分配一个计数器,用来记录方法调用的次数,当次数达到指定的阈值则认为该方法为热点代码。

    HotSpot虚拟机采用的是基于计数器的热点探测,它为每个方法准备了两类计数器:方法调用计数器回边计数器

    • 方法调用计数器:

      方法调用计数器的作用是:用于统计方法被调用的次数

      方法调用计数器触发即时编译的过程,如下图: 在这里插入图片描述
      过程描述:方法调用的时候,首先检查是否存在JIT编译的版本,如果存在,则直接该版本的本地代码执行。如果不存在,则方法调用计数器+1,然后判断此时方法调用计数器+回边计数器的总和是否超过了阈值,如果没超过则会继续解释执行。如果超过了,则会向编译器发起一次编译请求,默认情况下,此时的虚拟机不会等待编译器返回编译结果,会继续使用解析器去执行。当编译完成后,会自动修改方法的入口地址。

      在这个过程当中其实还涉及到两个概念,这里也简单介绍下,那就是:热度衰减半衰周期

      方法调用计数器统计的总数并不是方法被调用的绝对次数,而是在一段时间内方法被调用的次数。当在一个时间段内方法被调用的次数无法达到可触发即时编译的阈值时,则会将计数减半,这个过程就是热度衰减,而这个时间段则称为半衰周期

    • 回边计数器

      回边计数器的作用是:用于统计方法中循环体执行的次数

      什么是回边指令呢?字节码中那些控制流向后跳转的指令称为回边指令

      回边计数器触发OSR编译的过程,如下图: 在这里插入图片描述
      过程描述:碰到回边指令的时候,首先检查是否存在已编译的版本,如果存在则直接执行本地代码。如果没有,则将回边计数器+1,然后判断此时回边计数器+程序调用计数器的总数是否超过了阈值,如果没超过则继续解释执行。如果超过了,则向编译器提交OSR编译器请求,并将回边计数器的值降低些,以此来保证后续循环可以解释执行(即编译器返回编译结果之前,循环继续,如果回边计数器的值不降低,则会触发又一次的OSR编译请求的提交)。

      与程序调用计数器不同,回边计数器触发OSR编译的过程中,没有热度衰减的过程,所以回边计数器中的值是循环执行的绝对次数。当回边计数器溢出的时候,会同时将方法调用计数器也调整为溢出状态,这样在下次方法被调用的时候就会触发标准的JIT编译过程了。

11.3 编译优化技术

11.3.1 公共子表达式消除

如果一个表达式E已经计算过了,再出现的时候什么变化都没有,则直接使用之前的结果,不需要再进行计算了。

11.3.2 数组边界检查消除

在一个循环体中,当编译器进行数据流分析后确定循环变量是在0~数组长度之间,则会消除掉数据边界检查。

11.3.3 方法内联

方法内联可以减少方法调用,同时也是其他优化手段的基础。

方法内联,简单来说就是将目标方法的代码“复制”到调用该方法的方法中,以此减少方法调用。还记得之前之前提到的解析和分派吗?只有那些非虚方法(静态方法、私有方法、父类方法、实例构造器、被final修饰的实例方法)能够确定只有唯一的版本,这样“复制”的时候也就只有唯一的选择,不会造成不知道“复制”哪个版本的情况出现。

而那些虚方法呢?它们只有在运行期才能确定调用哪个版本,所以编译期进行方法内联是无法确定内联哪个方法版本的

为了要解决上边这个问题,Java虚拟机团队引入了一个新技术 —— 类型继承关系分析(Class Hierarchy Analysis,CHA),它用于确定目前已加载的类中,某个接口是否多于一个实现,某个类是否有子类,子类是否为抽象类等信息。

下面对方法内联的过程进行如下总结:

  1. 判断方法是否为非虚方法,如果是非虚方法,则直接进行内联。
  2. 如果是虚方法,通过CHA来判断方法是否只有唯一一个版本,如果是只有一个唯一的版本,则进行内联(这种内联是激进优化,必须留有逃生门,称为守护内联(Guarded Inlining)),在之后的程序运行过程中如果虚拟机没有加载到能改变该方法该接收者继承关系的类,则方法内联会一直有效。否则,抛弃已编译的代码,退回到解释执行,或重新编译。
  3. 如果通过CHA查出来虚方法有多个版本,编译器还是会进行最后的尝试,采用内联缓存(Inline Cache)来进行方法内联。内联缓存是建立在方法正常入口之前的缓存。在方法调用之前,内联缓存中是空,当发生第一次方法调用的时候,内联缓存中会记录当前方法的版本,之后再调用该方法的时候会先比较之前的版本,如果版本一致,则方法内联一直有效,如果版本不一致,则方法内联撤销,通过虚方发表进行分派。
11.3.4 逃逸分析

逃逸分析是一种分析对象动态作用域的行为,它不是具体的优化手段,而是作为优化手段的辅助技术(与CHA类似)。逃逸分析分为:方法逃逸和线程逃逸。

  • 方法逃逸:对象在方法中定义后,被另外一个方法所引用,例如:对象作为另外一个方法的参数被传递。
  • 线程逃逸:方法中定义的对象被另外一个线程所引用,例如:将对象赋值给类变量或者可以被其他线程引用的实例变量。

通过对对象进行逃逸分析,如果我们能确定该对象不会被其他方法或线程访问到(即不会出现方法逃逸或线程逃逸的情况),则可以对对象进行如下优化:

  • 栈上分配

    如果对象没有发生方法逃逸,那么该对象所需的内存就可以在栈上分配,这样的好处是,可以随着栈帧出栈(即方法结束),对象所占用的内存就被释放掉了,同时也减少了垃圾收集器的压力。

    HotSpot虚拟机要想实现栈上分配很复杂,所以目前还没有实现这项优化

    Java语言中对象的内存只能在堆上分配,只有方法中的局部变量才有可能在栈

  • 同步消除

    如果对象没有发生线程逃逸,也就是说不会有其他线程访问该对象,不会出现竞争的情况,那么同步也就没有了意义了,所以同步指令可以消除。

  • 标量替换

    标量(Scalar)是不能再分解的最小数据,Java中的原始数据类型(例如:int、long … reference类型等)都不能再进一分解,它们就是标量。与之相反的,即可以继续分解的数据,称为聚合量(Aggregate),在Java中对象就是典型的聚合量。

    如果对象没有发生方法逃逸,且该对象可以被进一步分解,那么程序在真正执行的时候可能不会创建这个对象,而是直接创建该对象中的被方法使用到的成员变量,这个过程就是标量替换。

    标量替换可以看做是一种高度优化的后栈上分配,它相当于将对象分解为局部变量后再进行栈上分配

上一篇:《深入理解JAVA虚拟机(第2版)》- 第10章 - 学习笔记
下一篇:《深入理解JAVA虚拟机(第2版)》- 第12章 - 学习笔记

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VD1012 单节锂离子充电电池保护IC 2.8V过放电压 SOT-353小封装芯片

VD1012内置高精度电压检测电路和延迟电路以及内置MOSFET,是用于单节锂离子/锂聚合物可再充电电池的保护IC。 本IC适合于对1节锂离子/锂聚合物可再充电电池的过充电、过放电和过电流进行保护。 VD1012具备如下特点:高精度电压检测电路 过充电检测电压 4.…

曝字节 AI 硬件团队首款自研产品为智能耳机,与豆包联动;OpenAI 神秘新模型或将在两周内发布丨 RTE 开发者日报

开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

「OC」事件点击demo合集

「OC」事件点击demo合集 文章目录 「OC」事件点击demo合集前言可用鼠标移动的UIview突出的tabBar按钮扩大按钮的响应范围 前言 在前面通过学习事件响应流程,学习了许多新的内容,当然也学习了许多不同的用法,但在之前的文章之中并没有将运用到…

SDH信号的帧结构与复用

在OTN技术的学习中,涉及到了大量SDH的相关知识,例如映射,复用,开销等,所以简单的总结一下SDH的帧结构与复用用以辅助学习OTN技术。 DWDM(密波)大容量SDH(同步数字体系)的…

无源晶振的等效电路与电路结构解析

无源晶振,即不带内置振荡电路的晶体振荡器,它在电子设备中扮演着产生原始时钟频率的重要角色。以下是关于无源晶振的等效电路、电路结构及其关键参数的详细解析。 一、无源晶振的等效电路 无源晶振的等效电路主要包括静态电容C0、动态电容C1、谐振电阻R…

网络高级(学习)2024.9.11

目录 Modbus库函数 1.初始化和释放函数 2.功能函数 3.功能案例 Modbus RTU 1.特点 2.协议格式 3.编程思路 Modbus库函数 1.初始化和释放函数 modbus_t* modbus_new_tcp(const char *ip, int port) 功能:以TCP方式创建Modbus实例,并初始化 参数…

坐牢第三十八天(Qt)

1、使用Qt绘画事件处理画一个闹钟 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QPaintEvent>//画画处理事件 #include <QPainter>//画画 #include <QTime> //时间类 #include <QTimer>…

NVIDIA AI Workbench 让 Windows 上的 GPU 使用更加简便

NVIDIA AI Workbench 是一款免费的、用户友好型开发环境管理器&#xff0c;可在您选择的系统&#xff08;PC、工作站、数据中心或云&#xff09;上简化数据科学、ML 和 AI 项目。在 Windows、macOS 和 Ubuntu 上&#xff0c;您可以本地开发、测试项目和构建项目原型&#xff0c…

初识Maven:Java项目管理工具

实际开发中&#xff0c;伴随着项目规模的增长&#xff0c;依赖管理和构建自动化变得至关重要&#xff0c;一套标准化的项目结构有助于更好的开发项目、简化这项任务&#xff08;真的不是强迫症&#xff09; Maven&#xff0c;作为 Apache Software Foundation 维护的项目管理工…

如何获取MySQL数据表的列信息

在数据库管理中&#xff0c;了解表的结构是至关重要的。在MySQL中&#xff0c;我们可以通过几种方式来获取数据表的列信息。这不仅可以帮助我们更好地理解表的结构&#xff0c;还可以在编写查询时提供便利。以下是三种常用的方法来获取MySQL数据表的列信息。 使用 SHOW COLUMN…

HDMI彩条显示——FPGA学习笔记12

素材来自原子哥 一、HDMI简介 英文全称是High-Definition Multimedia Interface&#xff0c;即高清多媒体接口。 HDMI引脚解析&#xff08;A型&#xff09; HDMI工作连接图 HDMI工作原理 DVI编码输出示意图 二、TMDS编码&#xff08;最小化差分传输&#xff09; TMDS编码框图…

pip 阿里云镜像报错 certificate verify failed: unable to get local issuer certificate

在没有管理员身份&#xff0c;且有防火墙限制的电脑上&#xff0c;pip安装​python库包失败。​但是在普通的电脑上安装正常。​​报错内容如下&#xff1a; (SSS_web) C:\Users\HXAIYVQ>pip install flask -i https://mirrors.aliyun.com/pypi/simple/ Looking in indexes…

302.AI学术论文搜索工具的智能体验

Hey朋友们&#xff0c; 你是否曾在学术的海洋里迷失方向&#xff0c;为了找到一篇论文而苦苦挣扎&#xff1f; 就像在茫茫大海中寻找灯塔&#xff0c;我们渴望一盏明灯&#xff0c;指引我们前行。 别担心&#xff0c;今天我来给你介绍一个超级给力的工具——302.AI学术论文…

求教0基础入门大模型的学习路线?

0基础入门大模型&#xff0c;transformer、bert这些是要学的&#xff0c;但是你的第一口不一定从这里咬下去。真的没有必要一上来就把时间精力全部投入到复杂的理论、各种晦涩的数学公式还有编程语言上&#xff0c;这样不仅容易让你气馁&#xff0c;而且特别容易磨光热情。当我…

如何系统的入门大模型?

对于刚开始接触大模型&#xff08;LLM&#xff09;的研究者来说&#xff0c;系统地学习和探索是非常重要的。以下是一个循序渐进的学习路径&#xff0c;帮助你高效地入门大模型的领域。 1、浏览基础资源与课程 首先&#xff0c;你可以通过阅读几篇公众号或知乎上的文章来了解大…

动态数字时钟屏保 提升桌面美化 电脑屏幕屏保软件

时钟屏保软件可以让你的电脑更有特色&#xff0c;当你离开电脑时候&#xff0c;屏保可以保护你的桌面隐私&#xff0c;还是比较有用的一款小软件&#xff0c;今天小编给大家推荐的这款可以实现动态数字时钟的屏保软件&#xff1a;芝麻时钟 &#xff08;下载地址&#xff1a;htt…

终端显示字体背景和字体颜色

【终端显示字体背景和字体颜色等使用用法】 在命令行下想要产生五颜六色的字体和背景&#xff0c;只需要加上一些颜色代码即可。 ANSI 标准规定了一种所有终端共享的指令集&#xff0c;并要求用 ASCII 的数字字符传递所有数值信息&#xff0c;用于控制 Linux 终端上的光标位置…

抓包分析ARP协议工作原理

目录 1. ARP 协议 2. 工作原理 3. ARP 协议报文格式 4. ARP 缓存的查看和修改 5. tcpdump 抓包分析 ARP 协议工作原理 5.1 搭建 2 台虚拟机 5.2 在主机 192.168.0.155 打开一个shell命令行开启抓包监听 5.3 在主机 192.168.0.155 打开另一个shell命令行 telnet 192.168.…

恢复二叉搜索树

题目 给你二叉搜索树的根节点 root &#xff0c;该树中的两个节点被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树。 进阶&#xff1a;使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗&#xff1f; 示例 1&#xff1a; 输入&…

超声眼镜波清洗机有用吗?真正好用的超声波清洗机推荐

随着时代的进步&#xff0c;人们对家居生活质量的追求也日益提高。尤其是对于珠宝、饰品、眼镜等小物件&#xff0c;长时间不使用后往往会积累灰尘和细菌&#xff0c;这些细菌隐藏在肉眼看不到的地方&#xff0c;它们对健康的影响不容忽视。幸运的是&#xff0c;超声波清洗机能…