JVM重要优化之——逃逸分析

JVM重要优化之——逃逸分析,第1张

JVM重要优化之——逃逸分析

今天来谈谈另一个同方法内联一样重要的优化方法——逃逸分析!

逃逸分析是一项比较前沿的优化技术,至今仍在发展中,但它也是JVM优化技术的一个重要方向。

那逃逸分析是什么?又是如何优化的?

逃逸分析:分析对象的动态作用域,简单来说就是根据对象的作用范围分析出不同的逃逸程度,目前逃逸程度由低到高分别为不逃逸、方法逃逸、线程(全局)逃逸。

何为对象不逃逸?
例如有一个类,

static class FormData{
        private Integer v1=1;
        private Float v2=2f;
        public Integer getV1() {
            return v1;
        }
        public void setV1(Integer v1) {
            this.v1 = v1;
        }
        public Float getV2() {
            return v2;
        }
        public void setV2(Float v2) {
            this.v2 = v2;
        }
}

有一个方法如下,在方法中创建了data 对象,然后data对象只在test1()方法中使用,类似这种只在本方法内创建使用的对象称为对象不逃逸。

public int test1(){
    FormData data = new FormData();
    int sum=data.getV1()+1;
    return sum;
}

何为方法逃逸?

public void test2(){
    FormData data = new FormData();
    test3(data);
}
public void test3(FormData data) {
    data.setV1(2);
    data.setV2(3f);
}

在上述代码中,test2()方法创建的的data 对象,然后将data对象传给test3方法调用,类似这种对象传递引用过程称为对象方法逃逸;

何为全局逃逸?

public class Demo {
    public static FormData formData;
    public FormData test4() {
        FormData data = new FormData();
        data.setV1(5);
        data.setV2(5f);
        return data;
    }
    public void test5() {
        formData = new FormData();
    }
} 

在上述的test4()或test5()的方法中,在其内创建的对象已逃出对应的方法,可以被外部线程全局使用,类似这种对象使用方法称为全局(线程)逃逸;

JVM如何对逃逸进行优化? 1.栈上分配:

我们都知道绝大多数对象都在内存堆上分配,而这个堆是对于线程来说是共享的,只要持有某个对象的引用,就能访问到对应的对象数据。正因为绝大部分对象都存活在堆中,所以java的JVM垃圾回收系统回收活动几乎都是在堆中进行,而垃圾回收常常会伴随着STW,并且对象的创建和回收都是十分消耗资源的 *** 作。因此,JVM做了相关的优化,对于一个确定不会发生全局(线程)逃逸的对象,会在栈上直接分配,随着方法栈帧出栈而销毁,这样就能大大提高资源的利用率。在我们的应用中,绝大部分情况都是使用这种可栈上分配类型的对象;

2.标量替换:

标量指的是在Java语言中的基本数据类型及reference类型等不能再分解成更小的单位来表示的数据。Java的绝大多数对象都是由标量构成的聚合量,如上方的FormData类的对象,一个对象里面包括V1 Integer类型和V2 Float类型的两个标量,这两个变量类型是不可再分解的。对于这样对象,JVM在执行程序过程中,有可能不会取创建真正的对象,而是直接成员变量来代替,例如

    public void test6(){
        // 如原本需要使用data对象  FormData data = new FormData();
        
        //实际运行时可能会使用如下标量直接代替
        Integer data.v1 = 1;
        Float data.v2 = 2f;
        ...
}

在程序中不进行创建对象,而是直接使用不可分解的标量去替换,这种优化方式称为标量替换,实际上标量替换是一种特殊的栈上分配方式,但是对逃逸程度分析要更高,它不允许对象逃逸出方法的范围。

3.同步消除:

线程同步本身是一个十分耗费资源的过程,其会使程序效率运行变低,因此如非需要进行线程同步,尽量不要使用线程同步。在JVM中,如果能通过逃逸分析,分析出一个变量对象不发生全局(线程)逃逸,这时肯定不会发生线程竞争,也不会引发安全问题,那么JVM会进行同步消除。例如:

//优化前
public void test7(){
    FormData data = new FormData();
    int i=1;
    int j=2;
    int sum;
    synchronized (data){
        sum=i+j;
        ...
    }
    System.out.println(sum);
}
//优化后
public void test7(){
    FormData data = new FormData();
    int i=1;
    int j=2;
    int sum;
    //此处对 synchronized同步进行消除 
    sum=i+j;
    ...
    System.out.println(sum);

}

总结:

逃逸分析是JVM中一项重要的编译期优化技术,早在1999年就有论文提出,但是HotSpot虚拟机在JDK6才实际应用,直到现在这项技术尚未完善,仍有许多需要改进的地方。其中最大的问题就是需要分析对象的逃逸的成本非常高,因为在实际Java应用程序中,绝大数对象是复杂对象,所以如今虚拟机使用的逃逸分析算法采用的都是不那么准确的分析算法,使得在逃逸分析的时间压力较小,也能达到一部分的优化目的。总的来说,逃逸分析是一项在JVM十分重要的优化技术,如以后取得更重大的突破,定会使Java的运行效率上一个新的层次。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5660438.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存