本文介绍: 面试管:在Java中新创建对象一定是在堆上分配内存吗?如果你的答案是“是的”那就需要看看这个文章了。

优质博文IT-BLOG-CN

面试管坑位:在Java中新创建对象一定是在堆上分配内存吗?如果你的答案“是的”那就需要看看这个文章了。

一、简介

逃逸分析Escape Analysis:是一个很重要的JIT优化技术用于判断对象是否会在方法外部访问到,也就是逃出方法作用域逃逸分析JIT编译器的一个步骤通过JIT我们能够确定哪些对象可以限制方法内部使用,不会逃逸外部然后可以对它们进行优化比如把它们分配在栈上而不是堆上,或者进行标量替换,把一个对象拆散成多个基本类型存储是一种可以有效减少Java程序同步负载内存堆分配垃圾回收压力的跨函数全局数据流分析算法通过逃逸分析Java Hotspot编译器能够分析出一个新对象引用使用范围,而决定是否要将这个对象分配到堆上

逃逸分析主要针对局部变量判断堆上分配的对象是否逃逸出方法的作用域。它同编译器优化原理指针分析和外形分析相关联。当变量或者对象)在方法中分配后,其指针可能返回或者全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针或者引用)的逃逸Escape通俗点讲,如果一个对象指针多个方法或者线程引用时,那么我们就称这个对象的指针发生了逃逸合理设计代码结构数据使用方式能够更好利用逃逸分析来优化程序性能我们还可以通过逃逸分析减少堆上分配对象的开销,提高内存利用率。

逃逸分析不是直接优化手段,而是代码分析手段。

二、逃逸分析的好处

【1】栈上分配,可以降低垃圾收集器运行频率
【2】同步消除,如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步
【3】标量替换,把对象分解一个个基本类型,并且内存分配不再是分配在堆上,而是分配在栈上。这样的好处有:减少内存使用,因为不用生成对象头。程序内存回收效率,并且GC频率也会减少。因此对于临时对象或短期使用的对象,尽量使用局部变量来存储,以减少对象逃逸的可能性。对于复杂数据结构,尽量使用基本类型数组集合类,以减少对象的分配和逃逸。
【4】使用final关键字来限制对象的可变性,这样JIT编译器更容易进行逃逸分析和优化

三、why

栈上分配Stack Allocations:在Java虚拟机中,Java堆上分配创建对象内存空间几乎是Java程序员知道的常识,Java堆中的对象对于各个线程都是共享和可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据虚拟机垃圾收集子系统回收堆中不再使用的对象,但回收动作无论是标记筛选出可回收对象,还是回收整理内存,都需要耗费大量资源。如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分配内存将会是一个很不错的主意,对象所占用内存空间就可以随栈帧出栈销毁在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着方法的结束自动销毁了,垃圾收集系统的压力将会下降很多。栈上分配可以支持方法逃逸,但不能支持线程逃逸。

标量替换Scalar Replacement 若一个数据已经无法再分解成更小的数据表示了,Java虚拟机中的原始数据类型都不能再进一步分解,那么这些数据就可以被称为标量相对的,如果一个数据可以继续分解,那它就被称为聚合AggregateJava中的对象就是典型的聚合量。如果把一个Java对象拆散,根据程序访问的情况,将其用到成员变量恢复为原始类型来访问,这个过程称为标量替换假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。将对象拆分后,除了可以让对象的成员变量在栈上(栈上存储的数据,很大机会被虚拟机分配至物理机器的高速寄存器中存储)分配和读写之外,还可以为后续进一步优化手段创建条件标量替换可以视作栈上分配的一种特例,实现简单不用考虑整个对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。

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

代码说明 但是在实际的应用程序中,尤其是大型程序中反而发现实施逃逸分析可能出现效果稳定的情况,或分析过程耗时但却无认开启选项。如果有需要用户可以使用参数-XX:+DoEscapeAnalysis手动开启逃逸分析法有效判别出非逃逸对象而导致性能下降。

public class EscapeTest {
    /**
     * JIT编译时会对代码进行逃逸分析
     * 并不是所有对象存放堆区,有的一部分在线程栈空间
     * Person没有逃逸
     */
    private static String alloc() {
        Person person = new Person();
        return person.toString();
    }

    /**
     * 同步省略(锁消除)JIT编译阶段优化,JIT经过逃逸分析之后发现无线安全问题,就会做锁消除
     */
    public void append(String str1, String str2) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(str1).append(str2);
    }

    /**
     * 标量替换
     */
    private static void test2() {
        Point point = new Point(1,2);
        System.out.println("point.x="+point.getX()+"; point.y="+point.getY());
        // 编译后的伪代码,也就是常说的内联后的样子
        // int x=1;
        // int y=2;
        // System.out.println("point.x="+x+"; point.y="+y);
    }
}

四、结论

关于逃逸分析的研究论文早在1999年就已经发表,但直到JDK 6HotSpot才开始支持初步的逃逸分析,而且到现在这项优化技术尚未足够成熟,仍有很大的改进余地。不成熟的原因主要是逃逸分析的计算成本非常高,甚至不能保证逃逸分析带来的性能收益会高于它的消耗。如果要百分之百准确地判断一个对象是否会逃逸,需要进行一系列复杂数据流敏感的过程间分析,才能确定程序各个分支执行时对此对象的影响前面介绍即时编译、提前编译优劣势时提到了过程间分析这种大压力的分析算法正是即时编译的弱项。可以试想一下,如果逃逸分析完毕后发现几乎找不到几个不逃逸的对象,那这些运行期耗用的时间就白白浪费了,所以目前虚拟机只能采用不那么准确,但时间压力相对较小的算法来完成分析。这个在JIT优化实战中有说明

jdk7开始默认开启逃逸分析。在Java代码运行时,可以通过JVM参数指 定是否开启逃逸分析:

XX:+DoEscapeAnalysis //表示开启逃逸分析 (jdk1.8默认开启)XX:DoEscapeAnalysis //表示关闭逃逸分析。XX:+EliminateAllocations //开启标量替换(默认打开)XX:+EliminateLocks //开启锁消除(jdk1.8默认开启)

开启逃逸与关闭逃逸的区别
【1】关闭逃逸:-XX:-DoEscapeAnalysis -XX:+PrintGC

long start = System.currentTimeMillis();
for(int i=0;i<5000000;i++){
    newObject();
}
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-start)+"毫秒");
Thread.sleep(100000);

结果:41毫秒一次GC并且有一百多万的垃圾回收。

[GC (Allocation Failure)  65536K->880K(251392K), 0.0013300 secs]
耗时41毫秒

num  #instances    #bytes       class name
1:   1088834       17868374     java.lang.Object

【2】开启逃逸:-XX:+DoEscapeAnalysis -XX:+PrintGC 只有4毫秒,没有GC提高了快10效率,并且堆中只有十几万。逃逸了

耗时4毫秒

num  #instances    #bytes       class name
1:   14534         2734633     java.lang.Object

可以发现一个逃逸和没逃逸的问题只要是对象有被方法外部或者全局用到那肯定会存在逃逸。当对象没有发生逃逸的时候虚拟机会对其进行优化。

原文地址:https://blog.csdn.net/zhengzhaoyang122/article/details/134225094

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_49893.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注