逃逸分析
逃逸分析
逃逸分析是“一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针”
在Java虚拟机的即时编译环境下,逃逸分析将判断新建的对象是否逃逸。判断逃逸的依据是:
- 对象是否被存入堆中(静态字段或堆中对象的实例字段)
- 对象是否被传入未知代码中。
前者很好理解,一旦对象放入堆中,其他线程就可以获得该对象的引用。即时编译器也因此无法追踪所有引用该对象代码的位置。
对于第二点,由于Java编译器的编译是以方法为单位的,对于方法中未被内联的方法调用,编译器会将其当作未知代码,因为无法确认该方法调用是否会将调用者或传入的参数存储至堆当中。所以可以把方法调用的调用者以及参数是逃逸的。
基于逃逸分析的优化
即时编译器可以根据逃逸分析的结果进行诸如锁消除、栈上分配以及标量替换的优化。
对于锁消除,如果即时编译器能够证明锁对象(指的就是要被加锁的对象)不逃逸,那么对该锁对象的加锁解锁操作是没有意义的。因为其他线程无法获得该锁对象,也不可能对其进行加锁操作,即时编译器可以消除对该对象的加锁解锁操作。
传统编译器仅需证明锁对象不逃逸出线程,便可以进行锁消除。由于 Java 虚拟机即时编译的限制,上述条件被强化为证明锁对象不逃逸出当前编译的方法。
注意,上述场景并不多见,因为很少在方法内,基于新构建的对象进行加锁操作。一般都是某个类的公共成员属性进行加锁。
逃逸分析优化更多的是用在新建对象操作转换成栈上分配或者标量替换。
Java 虚拟机中对象都是在堆上分配的,而堆上的内容对任何线程都是可见的。同时,Java 虚拟机需要对所分配的堆内存进行管理,并且在对象不再被引用时回收其所占据的内存。
如果逃逸分析能够证明某些新建的对象不逃逸,那么 Java 虚拟机完全可以将其分配至栈上, 并且在 new 语句所在的方法退出时,通过弹出当前方法的栈桢来自动回收所分配的内存空间。这样一来,我们便无须借助垃圾回收器来处理不再被引用的对象。
实际上,HotSpot 并没有采用在栈上分配,而是采用了一种标量替换的技术。标量,指的是仅能存储一个值的变量。标量替换这项优化技术,可以看成将原本对对象的字段的访问,替换为一个个局部变量的访问。即将原本在内存中连续分配的对象,拆成了一个个单独的字段。这些字段既可以存储在栈上,也可以直接存储在寄存 器中。而该对象的对象头信息则直接消失了,不再被保存至内存之中。