JVM之几种常见的JIT优化

2023-05-16

目录

一、公共子表达式消除(经典的JIT优化技术)

二、方法内联

三、逃逸分析

四、三种逃逸分析优化方式

         1、 对象的栈上内存分配

2、标量替换

3、同步锁消除

一、公共子表达式消除(经典的JIT优化技术)

        1、概述

        如果一个表达式E已经经过计算,并且从先前的计算到本次计算E中的所有变量值保持不变,那么E在此次计算中就是公共子表达式。此时只需要使用之前E的计算结果即可。

        2、分类

  • 局部公共子表达式消除:仅限于程序基本块(方法,循环等)中
  • 全局公共子表达式消除:包含多个基本块的优化。

        3、优化示例

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 公共子表达式消除
 */
public class publicExtendsExpression {
    public static void main(String[] args) {
        for(int i=0;i<1000000;i++){
            sum(1,2,3);
        }
    }
    public static void sum(int a, int b, int c){
        /*计算d的值时,由于是循环执行,每次传入的参数又不变,那么JIT就会对该表达式中的公共子表达式进行优化*/
        int d=(b*c)*12+a+(a+b*c);//其中c*b为公共子表达式
        /*所以上式等同于:int d=E*12+a+(a+E)  -------消除
        *               int d=E*13+a*2      -------代码再优化*/
    }
}

二、方法内联

        1、概述

        在使用JIT进入即时编译时,将方法调用直接使用方法体中进行代码替换,减少方法调用时的压栈入栈的开销;同时为之后的一些优化手段提供条件。如果JVM检测到一些方法被频繁执行,就会把方法调用替换成方法本身。

        2、好处

        方法执行时会从方法区压入栈形成栈帧,当循环或者递归调用次数过多时,将会导致栈内存溢出(java.lang.StackOverflowError异常),而将方法的调用替换成方法体本身,可以减少压栈操作,节省资源。

        3、示例

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 方法内联
 */
public class MethodInAssociated {
    public static void main(String[] args) {
        long start1=System.currentTimeMillis();
        for (int i=0;i<10000000;i++){
            int e=alSum(1,2,3,4);//调用嵌套方法多次计算(JIT自动内联进行方法体替换)
        }
        long end1=System.currentTimeMillis();
        System.out.println("JIT内置方法内联时间花费="+(end1-start1));
        long start2=System.currentTimeMillis();
        for (int i=0;i<10000000;i++){
            int e=alSum2(1,2,3,4);//多次计算(方法体替换成内联后的方法体,模拟JIT方法内联)
        }
        long end2=System.currentTimeMillis();
        System.out.println("模拟JIT内置方法内联时间花费="+(end2-start2));
    }
    public static int alSum(int a,int b,int c,int d){
        return sum(a,b)+sum(c,d);//在alSum方法中嵌套调用sum方法
    }
    public static int  sum(int a,int b){
        return a+b;
    }
    public static int alSum2(int a,int b,int c,int d){
        return a+b+c+d;
    }
}

        经过多次测试,两种时间花费相差都在1毫秒内,说明JIT对代码进行了优化。减少了alSum()方法中对于sum()方法的入栈所带来的时间和性能上的消耗。 

三、逃逸分析

        1、概述

        逃逸分析是一种可以有效减少java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。

        2、作用

        通过逃逸分析,java HotSpot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆内存中(大多数情况下,我们都会认为对象在堆内存中,但其实这不是绝对的,逃逸分析就会改变这一“定论”)。

        3、方法逃逸

        分析对象的动态作用域,当一个对象在方法中被定义后,他可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。

        4、逃逸分析包括

  • 全局变量赋值逃逸
  • 方法返回值逃逸
  • 实例引用发生逃逸
  • 线程逃逸

        5、 示例(以方法返回值逃逸为例)

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 方法返回值逃逸
 */
public class ReturnEscapeAnalysis {
    public static void main(String[] args) {
        StringBuffer b=stringAdd("这是方法返回值","逃逸分析");
    }
    public static StringBuffer stringAdd(String s1,String s2){
        StringBuffer buffer=new StringBuffer();
        buffer.append(s1);
        buffer.append(s2);
        return buffer;
    }
}

        在上面的代码中,stringAdd()方法中的buffer对象作为返回值在主函数中被使用了,赋值给了对象b,那么此时,我们称buffer发生了方法返回值逃逸;若将stringAdd()方法的返回值改为String类型,然后return buffer.toString();那么此时buffer对象并没有也不能被外部所使用,所以就没有发生方法返回值逃逸。

        6、开启和关闭逃逸分析。

        在jkd1.7之后JVM默认开启逃逸分析。开启与关闭命令如下:

  • 开启配置:-XX:+DoEscapeAnalysis
  • 关闭配置:-XX:-DoEscapeAnalysis

        开启方式(IDEA):  先运行一次方法,构建一下代码,再配置。                  

四、三种逃逸分析优化方式

        1、 对象的栈上内存分配

               ① 一般情况下,对象和数组元素的内存分配是在堆内存上进行的。JIT编译器可以在编译期间根据逃逸分析结果,决定是否将对象的内存分配从堆内存转化为栈。这么做有两个好处:

  • 堆内存中的对象会涉及到垃圾回收操作,而垃圾回收是一个很耗性能的操作,而栈中的对象不会参与到垃圾回收机制。
  • 堆对于对象的存储消耗空间较大,堆中的一个对象包括对象头、实例数据、对齐填充;而栈中的对象只有实例数据。

               ②命令行查看java程序执行过程中堆内存内容命令:jps   和    jmap -histo [数字]

               ③示例

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 逃逸分析测试之栈上内存分配。
 */
public class EscapeAnalysis {
    public static void main(String[] args) {
        long start=System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            newObject();
        }
        long end =System.currentTimeMillis();
        System.out.println("expend:"+(end-start)+"ms");//未开启逃逸分析,expend=17ms,实例化一百万个Student对象。
                                                    //开启逃逸分析,expend=5ms,实例化100541个对象,几乎少了十倍!
        try {
            Thread.sleep(100000);//为什么要进行线程的等待:等待状态使得方法运行未结束,对象还存在堆内存中
                                       //才可以进行命令行分析,否则方法执行结束,堆内存就销毁了,无法进行对比。
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void newObject(){
        Student student=new Student();//方法返回值为void,所以对象student没有逃逸,进行栈上内存分配优化。
    }
    static class Student{
    }
}

                 (1)未开启逃逸分析时,耗时expend=17ms,此时java堆内存如下:

                此时可以发现,堆内存中实例化了一百万个Student对象,这是十分耗时且耗空间的。 

                 (2)开启逃逸分析后,耗时expend=5ms,此时java堆内存如下:

                此时堆内存中的Student对象只有约十一万个,相比没有开启逃逸分析减少了接近十倍,而速度也快了12ms,当数据量跟大时,差距还会更明显,优化的作用也就体现的更加重要。

2、标量替换

         ①什么是标量:标量是指一个无法再分解成更小数据更小单位的数据。

         ②概述:在JIT优化中,如果经过逃逸分析,发现一个对象没有逃逸,不会被外界访问到,那么就会将该对象拆解成若干个成员变量。

         ③示例    

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 标量替换
 */
public class ScalarReplace {
    public static void main(String[] args) {
        newScalar();
    }
    public static void newScalar(){
        Scalar s=new Scalar();
        int a= s.a;
        int b=s.b;
    }
    static class Scalar{
        private int a=1;
        private int b=0;
    }
}

       此时newScalar()方法中的对象s并没有发生逃逸,所以在其中使用对象s时,会将s替换成s中的两个成员变量a和b。

    public static void newScalar(){
        int a=1;
        int b=0;
    }

 3、同步锁消除

        ①概述:基于逃逸分析算法,当加锁(synchornized)的变量不会发生逃逸,是线程私有的,那么就没必要加锁,JIT就会对其进行优化,将同步锁去掉,以减少加锁和解锁造成的资源开销。

        ②示例:

package jvm.study;

/**
 * @author ghCode
 * @Email:2085264964@qq.com
 * 同步锁消除
 */
public class ReturnEscapeAnalysis {
    public static void main(String[] args) {
        StringBuffer b=stringAdd("同步锁","消除");
    }
    public static String stringAdd(String s1,String s2){
        StringBuffer buffer=new StringBuffer();
        buffer.append(s1);
        buffer.append(s2);
        return buffer.toString();
    }
}

        在StringBuffer类的apeend()方法中,通过查看源码可以发现加了同步锁,在上面的代码中,buffer对象没有发生逃逸,所以在JIT编译时会将append的同步锁去掉。

         

  

        

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM之几种常见的JIT优化 的相关文章

  • JVM CPU 峰值故障排除

    我们在其中一台应用程序服务器上发现了一个有趣的 尽管相当严重 问题 在某个时间点 运行 Web 应用程序的 JVM 的 CPU 使用率开始上升 并持续上升 直到应用程序最终减慢到爬行 修复此问题的唯一方法是重新启动应用程序服务器软件 应用服
  • 在最近的 JVM 中,不可见引用仍然是一个问题吗?

    我正在读书Java 平台性能 http java sun com docs books performance 1st edition html JPAppGC fm html 遗憾的是 自从我最初提出这个问题以来 该链接似乎已经从互联网上
  • IntelliJ 调试:暂停整个虚拟机,然后进入单线程

    我正在调试一个具有大量线程的应用程序 我的断点设置为暂停整个虚拟机 当线程遇到其中一个断点时 我想使用 Step Over 但这似乎会恢复整个虚拟机 直到该步骤完成 如果我可以只单步执行到达断点的单个线程 那确实会有帮助 在 Intelli
  • 为什么 WPF 应用程序的默认平台目标是 Visual Studio x86 而不是 AnyCPU?

    当我在 Visual Studio 2012 中创建新的 WPF 应用程序时 平台目标和生成配置默认设置为 x86 为什么会这样呢 对于普通的 WPF 应用程序 没有任何对混合模式程序集的引用 使用 AnyCPU 是否存在任何危险 因此我的
  • 如何使用IntelliJ IDEA ThreadDumpVisualizer插件分析Java线程转储

    我正在寻找使用一些线程转储分析器来分析 Java 线程转储并安装了ThreadDumpVisualizerIntelliJ IDEA 插件 但不知道如何使用它 插件页面 https plugins jetbrains com plugin
  • 以编程方式设置最大 Java 堆大小

    有没有办法以编程方式设置最大 java 堆大小而不是作为 vm 参数 就像是 System getProperties put
  • 内存中的方法表示是什么?

    在思考一下 Java C 编程时 我想知道属于对象的方法如何在内存中表示 以及这一事实如何涉及多线程 是为内存中的每个对象单独实例化一个方法还是执行 同一类型的所有对象共享该方法的一个实例 如果是后者 执行线程如何知道哪个对象是 要使用的属
  • 是否可以使 java.lang.invoke.MethodHandle 与直接调用一样快?

    我正在比较性能MethodHandle invoke以及直接静态方法调用 这是静态方法 public class IntSum public static int sum int a int b return a b 这是我的基准 Stat
  • getResourceAsStream(file) 在哪里搜索文件?

    我很困惑getResourceAsStream 我的包结构如下 src net floodlightcontroller invoked getResourceAsStream here resources floodlightdefaul
  • Angular 2 引导选项 - AOT 与 JIT

    刚开始使用 Angular 2 Angular 2 中的各种 Bootstrapping 选项有哪些 为什么当我进行更改并刷新时 index html 只需要很少的时间来检索 HTML 标记 它们之间的区别 有两种选择 动态引导 使用的编译
  • JIT 与解释器

    我找不到 JIT 和解释器之间的区别 Jit 是解释器和编译器的中介 在运行时 它将字节代码转换为机器代码 JVM 或实际机器 下次 它从缓存中获取并运行 我对吗 解释器将直接执行字节码 而不将其转换为机器代码 是对的吗 我们电脑中真正的处
  • 即时编译与提前编译相比有何优点?

    我最近一直在思考这个问题 在我看来 最大的优势是JIT编译或多或少应该归因于中间格式 而抖动本身并不是生成代码的好方法 那么主要就是这些pro JIT我经常听到的编译参数 即时编译可实现更大的可移植性 这不是中间格式的原因吗 我的意思是 一
  • JVM HotSpot 上的 Java 异常计数器

    我想知道是否可以在不更改应用程序代码的情况下记录 JVM 级别上发生的每个异常 我所说的每个异常是指捕获和未捕获的异常 我想稍后分析这些日志并按异常类型 类 对它们进行分组 并简单地按类型对异常进行计数 我正在使用热点 也许有更明智的理由这
  • 使用 + 符号连接字符串

    今天我在读书Antonio 关于 toString 性能的博客 https antoniogoncalves org 2015 06 30 who cares about tostring performance 还有一段话 昨天曾经被认为
  • STS 无法在我的计算机上启动

    我试图在 eclipse 上设置 Spring mvc 项目 基本项目进展顺利 但是使用 Restful 服务 Jersey 等开始出现许多与依赖项相关的错误 所以我打算转到STS 我正在使用 STS 2 9 2 它给我 无法创建java虚
  • 强制jvm返回本机内存[重复]

    这个问题在这里已经有答案了 我时不时地运行需要大量内存的 eclipse 任务 因此 当任务运行时 jvm 会消耗大约 2 3GB 的 RAM 这是可以的 但是一旦 jvm 占用了该内存 它就不会释放它 并且我遇到了一种情况 堆中已用内存约
  • Scala REPL 中的递归重载语义 - JVM 语言

    使用 Scala 的命令行 REPL def foo x Int Unit def foo x String Unit println foo 2 gives error type mismatch found Int 2 required
  • 使用 javac 和 javax.tools.JavaCompiler 有什么区别?

    Maven 编译器插件文档states http maven apache org plugins maven compiler plugin 编译器插件用于编译项目的源代码 从 3 0 开始 默认编译器是 javax tools Java
  • 如何使用 Java 引用释放 Java Unsafe 内存?

    Java Unsafe 类允许您按如下方式为对象分配内存 但是使用此方法在完成后如何释放分配的内存 因为它不提供内存地址 Field f Unsafe class getDeclaredField theUnsafe Internal re
  • Java 中清除嵌套 Map 的好方法

    public class MyCache AbstractMap

随机推荐

  • git repo 拉取代码,报错gpg: Can‘t check signature: No public key问题

    qiufanzheng 64 qiufanzheng SJB6 rk356x linux repo init repo url 61 git 64 ashrdgit sh ieinet org vendor google aosp git
  • 利用栈来判断回文数

    一 xff1a 什么是回文数 xff1a 121 1221 这些数从前往后读 xff0c 从后往前读都是一样的 利用栈求解思路 xff1a 我们用一个数组来存储输入的字符串ss xff0c 然后我们从下标为零到最后 xff0c 取出元素放入
  • Laravel 简单使用七牛云服务

    前言 路漫漫其修远兮 xff0c 吾将上下而求索 学习 Laravel 之初觉得所有东西都很厉害的样子 xff0c 现在看来就是很厉害啊 xff01 最近在写一个项目上传的模块 xff0c 要上传图片到七牛云 xff0c 昨天看了一下午七牛
  • 前端知识点(三):Flex布局(弹性布局)

    1 定义flex布局 1 display flex 2 行内元素 display inline flex 父元素中设置为flex布局 xff0c 形成flex容器 子元素的float clear vertical align属性将失效 2
  • 网络服务---OSI七层参考模型及各层工作原理详解

    OSI网络模型概念 OSI模型 xff08 Open System Interconnection Reference Model xff09 是指国际标准化组织 ISO 提出的一个试图使各种计算机在世界范围内互连为网络的标准框架 xff0
  • 树莓派官方系统连接电脑(电视)显示器无信号输出的解决方法

    一 打开盘符中的config文件 二 对第五行进行修改 修改前 xff1a 将 hdmi safe 61 1 修改为 xff1a hdmi safe 61 1 overscan left 61 30 overscan right 61 30
  • Cannot read properties of null (reading ‘style‘)前端错误记录21/10/20

    前端错误记录 Vue报错Cannot read properties of null reading style Vue报错 Cannot read properties of null reading style 起因 xff1a Vue
  • Java获取国内各个地区实时天气

    获取国内各个地区实时天气 不废话直接上代码 span class token keyword public span span class token keyword static span span class token class n
  • MyBatis-Plus——条件构造器Wapper、QUeryWrapper、UpdateWrapper、LambdaQueryWrapper、LambdaUpdateWrapper(详解)

    目录 一 条件构造器简介 二 QueryWrapper组装查询条件 三 QueryWrapper组装排序条件 四 QueryWrapper组装删除条件 五 QueryWrapper实现修改功能 六 QueryWrapper条件的优先级 七
  • 左移和右移的算法

    lt lt 左移 1 运算规则 xff1a 按二进制形式把所有的数字向左移动对应的位数 xff0c 高位移出 舍弃 xff0c 低位的 空位补零 2 语法格式 xff1a 需要移位的数字 lt lt 移位的次数 例如 xff1a 3 lt
  • 1.Stm32F407系列之点亮LED灯

    点灯大师已上线 xff01 我们在测试一个开发板的好坏 xff0c 或者是验证一些小功能模块是否起作用的时候 xff0c 最简单的方法就是打印输出或者是用LED灯指示 想要控制LED灯的亮和灭 xff0c 只要我们配置好IO口 xff0c
  • 智能机器人软件工程师学习路线

    0 引言 很多朋友对机器人软件开发和人工智能感兴趣 xff0c 不知道怎么学习 xff0c 传智播客武汉校区在今年3月份开设了一期智能机器人软件开发工程师就业班 xff0c 在这里我把就业班的学习曲线给大家介绍一下 0基础小白也能学会的人工
  • 2021-09-29 java命名规范、常量、变量简记

    文章目录 前言一 标识符 关键字 命名规则二 常量 变量 1 常量简介2 变量简介总结 前言 在刚入门java时 xff0c 应该养成代码规范书写的好习惯 xff0c 不应该随意命名 xff0c 符合统一的命名规则 xff0c 也会使别人在
  • Spring 学习笔记(十)渲染 Web 视图 (Apache Tilesa 和 Thymeleaf)

    使用Apache Tiles视图定义布局 为了在Spring中使用Tiles xff0c 需要配置几个bean 我们需要一个TilesConfigurer bean xff0c 它会负责定位和加载Tile定义并协调生成Tiles 除此之外
  • 2021-10-13 关于参数校验及@Valid和@RequestBody注解的组合使用

    一 前言 xff1a 学会并熟悉注解的使用 xff0c 在开发过程中 xff0c 是可以提高效率和简化工作复杂程度的 xff0c 也是会逐渐称为主要编码方式之一 二 1 64 RequestBody注解 xff1a 该注解在处理控制层的请求
  • vue之VueCli4的安装及使用

    一 前言 Vue CLI 是一个基于 Vue js 进行快速开发的完整系统 xff0c 提供 xff1a 通过 64 vue cli 实现的交互式的项目脚手架 通过 64 vue cli 43 64 vue cli service glob
  • 浅谈Mysql数据库

    一 为什么要使用数据库 xff1f 使用一个东西 xff0c 就要清楚它的功能价值 xff0c 才能更好的利用它 xff0c 使我们在工作生活中游刃有余 关于数据库的使用 xff0c 好多人会说 xff0c 一个数据库就是好多张表 xff0
  • java自定义一个数组类(封装多种方法)

    一 自定义数组类的动机 java给定的数组为静态的 xff0c 我们是无法对齐进行灵活的操作 xff0c 比如指定位置添加元素 xff0c 删除元素 xff0c 判断是否非空等 xff0c 于是我们便需要利用 面向对象 的设计模式 xff0
  • 关于JVM(基本常识)

    目录 一 JVM是什么 1 概述 二 为什么要用JVM 1 java程序的执行流程 2 JVM的架构 一 JVM是什么 1 概述 关于JVM xff0c 在百度上的解释为 xff1a JVM是Java Virtual Machine xff
  • JVM之几种常见的JIT优化

    目录 一 公共子表达式消除 xff08 经典的JIT优化技术 xff09 二 方法内联 三 逃逸分析 四 三种逃逸分析优化方式 1 对象的栈上内存分配 2 标量替换 3 同步锁消除 一 公共子表达式消除 xff08 经典的JIT优化技术 x