• 深入了解JVM之⭐《优化JIT》⭐


    本文主要分析JVM的功能《JIT》从java日志中分析

    👉👉运行环境JDK17👈👈

    本文侧重点不是提前编译与即时编译的优缺点,而是在JVM中的执行

    一、 简介JIT

    Java第一课都告诉我们:Java是无关平台的。换一种描述Java是面向JVM的,通常我们写出的程序要像被计算机识别并运行,需要编译成机器码,而Java代码并不是直接编译成机器码而是字节码,这意味着如果想要被计算机运行代码需要经过一层编译:。

    在这里插入图片描述

    那么每执行一个方法,都需要从字节码编译成机器码才能被运行,这样子的执行效率是较低的,因为每次都需要编译。

    由此JIT诞生,JIT(just in time)的缩写, 也就是即时编译,在运行Java程序的时候,通过判断热点代码,对执行频率高的代码进行即时编译,直接编译成机器码,后续不需要再从字节码编译成机器码才能执行,从而提高执行效率。

    二、 条件

    上问聊到热点代码会被"即时编译"成机器码,下文解答什么条件下成为热点代码。

    热点探测技术(Hot Spot Detection)

    主要有两种方案

    1. 基于采样的热点探测

    采用这种方法的虚拟机会周期性地检查各个线程的栈顶,如果发现某些方法经常出现在栈顶,那这个方法就是“热点方法”。这种探测方法的好处是实现简单高效,还可以很容易地获取方法调用关系(将调用堆栈展开即可),缺点是很难精确地确认一个方法的热度,容易因为受到线程阻塞或别的外界因素的影响而扰乱热点探测。

    1. 基于计数器的热点探测

    采用这种方法的虚拟机会为每个方法(甚至是代码块)建立计数器,统计方法的执行次数,如果执行次数超过一定的阀值,就认为它是“热点方法”。这种统计方法实现复杂一些,需要为每个方法建立并维护计数器,而且不能直接获取到方法的调用关系,但是它的统计结果相对更加精确严谨。

    Hotspot选择的热点探测技术

    在HotSpot虚拟机中使用的是第二种——基于计数器的热点探测方法,因此它为每个方法准备了两个计数器:方法调用计数器和回边计数器。在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。
    👉https://blog.csdn.net/Genmer/article/details/119355224

    编译阈值(-XX:CompileThreshold,client模式下默认是1500,server模式默认是10000)

    三、优化

    编译记录

    首先我们看看JVM的JIT日志:1w个HTTP请求后,打印出的日志信息
    👇JVM配置:打印jit日志

    -Xlog:jit+compilation=debug:file=D:\gclog\gc.log:level,tags,time,uptime,pid
    
    • 1

    在这里插入图片描述

    在1w个请求后的日志里可以直接看出,有8884条即时编译记录,因为使用的是Netty作为容易所以能看到netty的编译记录较多,当然排名靠前的基本都是JDK相关。

    在这里插入图片描述

    优化分析
    1. 存储空间

    ⭐1w条http请求共花费了66秒,但Jit高达8千多个方法⭐
    再看看Jit的空间:JVM打印出的日志可以看出真个JIT空间是很大了,48M对热点代码全部编译成机器码,我像也是绰绰有余
    👇JVM打印配置

    -Xms500M -Xms500M -Xlog:codecache+sweep*=trace:file=D:\gclog\gc.log:level,tags,time,uptime,pid
    
    • 1

    在这里插入图片描述

    在调用4w次接口后也未发现清理日志,可见是空间足够无需过度优化

    2. 判定条件

    判断条件主要有两,被判定为热点代码的阈值,卸载阈

    -XX:CompileThreshold=1000  //方法调用计数阈值
    -XX:-TieredCompilation  //关闭分层编译
    -XX:OnStackReplacePercentage=1000  //回边计数阈值
    -XX:InitialCodeCacheSize=80M //初始CodeCache大小
    -XX:ReservedCodeCacheSize=200M  //最大大小
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在笔者花费5小时方向优化后,性能反倒是倒退👆,有些参数概念还需读者自己去阅读文章
    结论:或许关闭分层编译峰值会更好,但均衡下分层编译(Jit)反而对接口TPS更高,无需过度优化

    四、结论

    如果希望项目拥有更好的峰值性能,全项目静态编译,读者可以去了解一下GraalVMSpring Native

    -EOF-

  • 相关阅读:
    安卓集成新版推送SDK与旧版本账号SDK发生依赖冲突,如何解决?
    【Spark】RDD、DataFram、DataSet的比较与使用
    C++——内存管理
    知识工程复习之十八类重点问题(8-12)
    C# 通过winmm枚举音频设备
    2022“杭电杯”中国大学生算法设计超级联赛(第一场) to be continued
    1151:素数个数
    Error: EMFILE: too many open files : 往服务器一次性传输文件数太多
    K8S篇之k8s containerd模式fail to pull image certificate signed by unknown authority
    第8天:可变与不可变类型、字典、元组与集合的内置方法
  • 原文地址:https://blog.csdn.net/qq_35040959/article/details/127702398