JVM逃逸分析代码优化-对象栈上分配,同步省略,标量替换

什么是逃逸?

逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。正常的方法调用中,方法体中创建的对象将在执行完毕之后,将回收其中创建的对象;故由于无法回收,即成为逃逸。

栈上分配

故名思议就是在栈上分配对象,其实目前Hotspot并没有实现真正意义上的栈上分配,实际上是标量替换。

普通对象在堆中分配,各线程共享。但有GC消耗。
当确定对象不会发生方法逃逸时,可在线程栈上分配对象。此时对象生命周期和方法相同,随栈帧出栈时即可销毁,不需要GC了。


    /***
     * user 没有逃逸出method1方法,jvm可以优化成栈上分配
     */
    private static void method1(){
        User user = new User();
        //TODO
        user=null;
    }

    /**
     * sb引用return出方法,可能被其他线程调用,因此发生了逃逸
     */
    private static StringBuffer method2(){
        StringBuffer sb=new StringBuffer();
        sb.append("zhang");
        sb.append("yukang");
        return sb;
    }

    /**
     * 如果method2不想让他发生逃逸,可以使用该方法
     * @return
     */
    private static String method3(){
        StringBuffer sb=new StringBuffer();
        sb.append("zhang");
        sb.append("yukang");
        return sb.toString();
    }

同步省略


    private static void method(){
        Object o=new Object();
        synchronized (o){
            //TODO
            System.out.println("do something");
        }
    }

    //以上的代码可以优化成下方的代码
    //因为method的方法调用的时候是方法每一线程的栈帧里的,因此每一个线程的Object 对象o
    //都是不一样的,因此此时加锁就毫无意义了
    private static void method2(){
        Object o=new Object();
        System.out.println("do something");
    }

标量替换

  • 标量
    一个数据无法再分解为更小的数据来表示了,Java 虚拟机中的基本数据类型 byte、short、int、long、boolean、char、float、double 以及 reference 类型等,都不能再进一步分解了,这些就可以称为标量。
  • 聚合量
    一个数据可以继续分解,就称为聚合量。对象就是最典型的聚合量。
    如果把一个 Java 对象拆散,根据程序访问的情况,将其使用到的成员变量恢复到基本数据类型来访问,就叫标量替换。
  • 替换过程
    如果逃逸分析可以证明一个对象不会被外部访问,并且这个对象可以拆散的话,那程序真正执行时将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来替代。将对象拆分后,除了可以让对象的成员变量在栈上分配和读写外(栈上存储的数据,有很大概率会被虚拟机分配至物理机器的高速寄存器中存储),还可以为后续的进一步优化手段创造条件。
package com.coderman.heap;

/**
 * 标量替换
 * @Author zhangyukang
 * @Date 2020/5/30 17:36
 * @Version 1.0
 **/
public class ScalarReplacementTest {


    public static void main(String[] args) {
        method();
    }

    private static void method(){
        Point point = new Point(10,20);
        System.out.println("point.x="+point.x+"\tpoint.y="+ point.y);
    }
}

//Point是一个聚合量,在经过逃逸分析后,发现对象没有放生逃逸,那么JVM将这个聚合量分解成标量
//int x. int y 
class Point{
    public int x;
    public int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

逃逸分析参数设置

-XX:+DoEscapeAnalysis//使用
-XX:-DoEscapeAnalysis//不用
-XX:+EliminateAllocations//开启标量替换,默认是打开的
# Java   JVM     

评论

公众号:mumuser

企鹅群:932154986

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×