论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
qidao123.com技术社区-IT企服评测·应用市场
»
论坛
›
职场与人生
›
IT职场那些事
›
Java核心技能口试精讲 第35讲 | JVM优化Java代码时都做 ...
Java核心技能口试精讲 第35讲 | JVM优化Java代码时都做了什么? ...
张国伟
论坛元老
|
2025-4-28 15:00:44
|
显示全部楼层
|
阅读模式
楼主
主题
1642
|
帖子
1642
|
积分
4926
第35讲 | JVM优化Java代码时都做了什么?
我在专栏上一讲介绍了微基准测试和相关的注意事项,其核心就是避免JVM运行中对Java代码的优化导致失真。以是,体系地理解Java代码运行过程,有利于在实践中举行更进一步的调优。
今天我要问你的问题是,JVM优化Java代码时都做了什么?
与以往我来给出典型答复的方式不同,今天我邀请了隔壁专栏 《深入拆解Java虚拟机》 的作者,同样是来自Oracle的郑雨迪博士,让他以JVM专家的身份去思考并答复这个问题。
来自JVM专栏作者郑雨迪博士的答复
JVM在对代码执行的优化可分为运行时(runtime)优化和即时编译器(JIT)优化。运行时优化重要是表明执行和动态编译通用的一些机制,比如说锁机制(如偏斜锁)、内存分配机制(如TLAB)等。除此之外,另有一些专门用于优化表明执行效率的,比如说模版表明器、内联缓存(inline cache,用于优化虚方法调用的动态绑定)。
JVM的即时编译器优化是指将热门代码以方法为单位转换成呆板码,直接运行在底层硬件之上。它采用了多种优化方式,包括静态编译器可以利用的如方法内联、逃逸分析,也包括基于程序运行profile的投机性优化(speculative/optimistic optimization)。这个怎么理解呢?比如我有一条instanceof指令,在编译之前的执行过程中,测试对象的类不停是同一个,那么即时编译器可以假设编译之后的执行过程中还会是这一个类,并且根据这个类直接返回instanceof的结果。如果出现了其他类,那么就扬弃这段编译后的呆板码,并且切换回表明执行。
当然,JVM的优化方式仅仅作用在运行应用代码的时候。如果应用代码本身壅闭了,比如说并发时等待另一线程的结果,这就不在JVM的优化范畴啦。
Java核心技能口试精讲
附上资源MP4下载地址
我用夸克网盘分享了「1001-Java核心技能口试精讲(更新完毕)」,点击链接即可生存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。
链接:https://pan.quark.cn/s/9b6c06b216fd
失效访问:https://cowcowit.com/course/324/325
考点分析
感谢郑雨迪博士从JVM的角度给出的答复。今天这道口试题在专栏里有不少同砚问我,也是会在口试时被口试官刨根问底的一个知识点,郑博士的答复已经非常全面和深入啦。
大多数Java工程师并不是JVM工程师,知识点总归是要落地的,口试官很有可能会从实践的角度探究,比方,如安在生产实践中,与JIT等JVM模块举行交互,落实到怎样真正举行实际调优。
在今天这一讲,我会从Java工程师一样平常的角度出发,侧重于:
从整体去了解Java代码编译、执行的过程,目的是对根本机制和流程有个直观的熟悉,以保证可以大概理解调优选择背后的逻辑。
从生产体系调优的角度,谈谈将JIT的知识落实到实际工作中的可能思路。这里包括两部门:怎样网络JIT相关的信息,以及具体的调优手段。
知识扩展
首先,我们从整体的角度来看看Java代码的整个生命周期,你可以参考我提供的示意图。
我在 专栏第1讲 就已经提到过,Java通过引入字节码这种中间表达方式,屏蔽了不同硬件的差别,由JVM负责完成从字节码到呆板码的转化。
通常所说的编译期,是指javac等编译器或者相关API等将源码转换成为字节码的过程,这个阶段也会举行少量类似常量折叠之类的优化,只要利用反编译工具,就可以直接查看细节。
javac优化与JVM内部优化也存在关联,究竟它负责了字节码的天生。比方,Java 9中的字符串拼接,会被javac更换成对StringConcatFactory的调用,进而为JVM举行字符串拼接优化提供了统一的入口。在实际场景中,还可以通过不同的 策略 选项来干预这个过程。
今天我要讲的重点是
JVM运行时的优化
,在通常环境下,编译器和表明器是共同起作用的,具体流程可以参考下面的示意图。
JVM会根据统计信息,动态决定什么方法被编译,什么方法表明执行,即使是已经编译过的代码,也可能在不同的运行阶段不再是热门,JVM有必要将这种代码从Code Cache中移除出去,究竟其大小是有限的。
就如郑博士所答复的,表明器和编译器也会举行一些通用优化,比方:
锁优化,你可以参考我在 专栏第16讲 提供的表明器运行时的源码分析。
Intrinsic机制,或者叫作内建方法,就是针对特别重要的底子方法,JDK团队直接提供定制的实现,利用汇编或者编译器的中间表达方式编写,然后JVM会直接在运行时举行更换。
这么做的理由有许多,比方,不同体系结构的CPU在指令等层面存在着差别,定制才华充分发挥出硬件的本领。我们一样平常利用的典型字符串操作、数组拷贝等底子方法,Hotspot都提供了内建实现。
而
即时编译器(JIT)
,则是更多优化工作的负担者。JIT对Java编译的根本单位是整个方法,通过对方法调用的计数统计,甄别出热门方法,编译为本地代码。别的一个优化场景,则是最针对所谓热门循环代码,利用通常说的栈上更换技能(OSR,On-Stack Replacement,更加细节请参考 R大的文章),如果方法本身的调用频度还不够编译标准,但是内部有大的循环之类,则还是会有进一步优化的价值。
从理论上来看,JIT可以看作就是基于两个计数器实现,方法计数器和回边计数器提供给JVM统计数据,以定位到热门代码。实际中的JIT机制要复杂得多,郑博士提到了 逃逸分析、 循环展开、方法内联等,包括前面提到的Intrinsic等通用机制同样会在JIT阶段发生。
第二,有哪些手段可以探查这些优化的具体发生环境呢?
专栏中已经陆连续续介绍了一些,我来简朴总结一下并补充部门细节。
打印编译发生的细节。
-XX:+PrintCompilation
复制代码
输出更多编译的细节。
-XX:UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:LogFile=<your_file_path>
复制代码
JVM会天生一个xml形式的文件,别的, LogFile选项是可选的,不指定则会输出到
hotspot_pid<pid>.log
复制代码
具体格式可以参考Ben Evans提供的 JitWatch 工具和 分析指南。
打印内联的发生,可利用下面的诊断选项,也必要明确解锁。
-XX:+PrintInlining
复制代码
怎样知晓Code Cache的利用状态呢?
许多工具都已经提供了具体的统计信息,比如,JMC、JConsole之类,我也介绍过利用NMT监控其利用。
第三,我们作为应用开发者,有哪些可以触手可及的调优角度和手段呢?
调整热门代码门限值
我曾经介绍过JIT的默认门限,server模式默认10000次,client是1500次。门限大小也存在着调优的可能,可以利用下面的参数调整;与此同时,该参数还可以变相起到降低预热时间的作用。
-XX:CompileThreshold=N
复制代码
许多人可能会产生疑问,既然是热门,不是早晚会到达门限次数吗?这个还真未必,由于JVM会周期性的对计数的数值举行衰减操作,导致调用计数器永久不能到达门限值,除了可以利用CompileThreshold得当调整大小,另有一个办法就是关闭计数器衰减。
-XX:-UseCounterDecay
复制代码
如果你是利用debug版本的JDK,还可以利用下面的参数举行试验,但是生产版本是不支持这个选项的。
-XX:CounterHalfLifeTime
复制代码
调整Code Cache大小
我们知道JIT编译的代码是存储在Code Cache中的,必要注意的是Code Cache是存在大小限制的,而且不会动态调整。这意味着,如果Code Cache太小,可能只有一小部门代码可以被JIT编译,其他的代码则没有选择,只能表明执行。以是,一个潜在的调优点就是调整其大小限制。
-XX:ReservedCodeCacheSize=<SIZE>
复制代码
当然,也可以调整其初始大小。
-XX:InitialCodeCacheSize=<SIZE>
复制代码
注意,在相对较新版本的Java中,由于分层编译(Tiered-Compilation)的存在,Code Cache的空间需求大大增加,其本身默认大小也被提高了。
调整编译器线程数,或者选择得当的编译器模式
JVM的编译器线程数量与我们选择的模式有关,选择client模式默认只有一个编译线程,而server模式则默认是两个,如果是当前最普遍的分层编译模式,则会根据CPU内核数量计算C1和C2的数值,你可以通过下面的参数指定的编译线程数。
-XX:CICompilerCount=N
复制代码
在强劲的多处理器环境中,增大编译线程数,可能更加充分的利用CPU资源,让预热等过程更加快速;但是,反之也可能导致编译线程争抢过多资源,尤其是当体系非常繁忙时。比方,体系部署了多个Java应用实例的时候,那么减小编译线程数量,则是可以考虑的。
生产实践中,也有人推荐在服务器上关闭分层编译,直接利用server编译器,固然会导致稍慢的预热速度,但是可能在特定工作负载上会有微小的吞吐量提高。
其他一些相对边界比较混淆的所谓“优化”
比如,减少进入安全点。严格说,它远远不但是发生在动态编译的时候,GC阶段发生的更加频仍,你可以利用下面选项诊断安全点的影响。
-XX:+PrintSafepointStatistics ‑XX:+PrintGCApplicationStoppedTime
复制代码
注意,在JDK 9之后,PrintGCApplicationStoppedTime已经被移除了,你必要利用“-Xlog:safepoint”之类方式来指定。
许多优化阶段都可能和安全点相关,比方:
在JIT过程中,逆优化等场景会必要插入安全点。
常规的锁优化阶段也可能发生,比如,偏斜锁的设计目的是为了避免无竞争时的同步开销,但是当真的发生竞争时,打消偏斜锁会触发安全点,是很重的操作。以是,在并发场景中偏斜锁的价值实在是被质疑的,经常会明确发起关闭偏斜锁。
-XX:-UseBiasedLocking
复制代码
重要的优化手段就介绍到这里,这些方法都是平凡Java开发者就可以利用的。如果你想对JVM优化手段有更深入的了解,发起你订阅JVM专家郑雨迪博士的专栏。
一课一练
关于今天我们讨论的题目你做到胸有定见了吗? 请思考一个问题,怎样程序化验证final关键字是否会影响性能?
请你在留言区写写你对这个问题的思考,我会选出经过认真思考的留言,送给你一份学习奖励礼券,欢迎你与我一起讨论。
你的朋友是不是也在预备口试呢?你可以“请朋友读”,把今天的题目分享给好友,大概你能帮到他。
点击下方图片进入JVM专栏
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
张国伟
论坛元老
这个人很懒什么都没写!
楼主热帖
XAF新手入门 - 类型子系统(Types Info ...
MyBatis 查询数据库
JAVA 装箱拆箱--到底指什么呢? ...
[NOI2010] 航空管制
RabbitMQ入门 -- 阿里云服务器安装Rabb ...
ThinkPHP5 远程命令执行漏洞
HarmonyOS(鸿蒙)开发一文入门 ...
基于 Dubbo Admin 实现同机房/区域优先 ...
浅入浅出 1.7和1.8的 HashMap
Prometheus配置Basic Auth进行安全防护 ...
标签云
渠道
国产数据库
集成商
AI
运维
CIO
存储
服务器
快速回复
返回顶部
返回列表