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