逃逸分析、栈上分配、同步省略、标量替换

逃逸分析

        逃逸分析只有在server模式下才可以开启逃逸分析,可以通过"-server"启动server模式

C:\Users\admin>java -version
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

        逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术,它与类型继承关系分析一样,并不是直接优化代码的手段,而是为其他优化措施提供依据的分析技术
        
逃逸分析的基本原理是:分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种称为方法逃逸;甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸;从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。

会发送逃逸的场景

// 快速判断一个对象是否发送了逃逸,看这个对象的实例是否可能在方法外被调用

// 1、作为方法的返回
    public Data getDate(){
        return new Data();
    }
// 2、对于成员变量的赋值操作
    public Data data = null;
    
    public void getDate(){
        data = data == null ? new Data() : data; 
    }
// 3、引用了成员变量的值
    public Data data = new Data();
    
    public void useData(){
        Data d = data;
    }

在JDK 6u23(或者直接说在JDK7及以后的) 版本中,默认开启了逃逸分析。如果使用的是早期的版本,可以使用“-XX:+DoEscapeAnalysis”显式的开启逃逸分析。通过“-XX:+PrintEscapeAnalysis”查看逃逸分析的筛选结果

如果发生了逃逸,就可能将对象分配在栈上(栈上分配)

栈上分配

如果确定一个对象不会逃逸出线程之外,栈上分配可以支持方法逃逸,但不能支持线程逃逸。

例子:

没有开启逃逸分析
        参数:-Xms3G -Xmx3G -XX:+PrintGCDetails -XX:-DoEscapeAnalysis

开启了逃逸分析
        参数:-Xms3G -Xmx3G -XX:+PrintGCDetails

        备注:如果对象是在栈上分配,那么对象会随着方法的出栈消失。栈是没有垃圾回收的,只有入栈和出栈。

 同步消除

        同步消除(Synchronization Elimination):线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以安全地消除掉。这种情况针对的是synchronized锁,而对于Lock锁,则JVM并不能消除。

例子:   

        // 我们不从JVM的角度也知道,这个锁是没用的,还影响效率,可以去除
        // 加了jvm也会去除
        Data data = new Data();
        synchronized (data){
            data.setId(1);
        }

        备注:逃逸分析是在java文件加载成字节码文件之后才进行的,所以在字节码文件中仍然可看的锁相关的内容 

 

 标量替换

        标量:无法再分解成更小数据的数据,基本数据类型。
        聚合量:非标量,引用数据类型,比如对象
在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。将对象拆分后,可以让对象的成员变量在栈上分配的和读写,标量替换可以视作栈上分配的一种特例,实现更简单(不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内

        Data data = new Data();
        data.setId(1);
        data.setContent("2");

        //     ||
        //     ||
        //     \/

        int id = 1;
        String content = "2";

 标量替换默认开启:也可以使用参数"-XX:+EliminateAllocations"来显示的开启和关闭

例子

开启
        参数:

关闭
        参数: -Xms256M -Xmx256M -XX:+PrintGC -XX:-DoEscapeAnalysis -XX:-EliminateAllocations

发现:开启了历时短,而且没有GC。如果 -XX:-DoEscapeAnalysis 但是 -XX:+EliminateAllocations也不会开启

总结

        逃逸分析还不成熟,直到JDK 6,HotSpot才开始支持初步的逃逸分析,而且到现在这项优化技术尚未足够成熟,仍有很大的改进余地。不成熟的原因主要是逃逸分析的计算成本非常高。所以目前虚拟机只能采用不那么准确,但时间压力相对较小的算法来完成分析。
        以上的例子,实际上都是标量替换,把对象打散存储在局部变量表中。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值