逃逸分析

  • 逃逸分析
  • 什么是逃逸分析?
  • 对象逃逸的几种状态
  • 1、全局逃逸
  • 2、参数逃逸
  • 3、没有逃逸
  • 逃逸分析优化
  • 1、锁消除
  • 2、标量替换
  • 3、栈上分配
  • 总结


逃逸分析

什么是逃逸分析?

逃逸分析是Hotspot虚拟机为了提升并优化jvm性能,通过分析创建对象的使用范围,来判断是否为该对象在jvm内存上分配空间的技术。简单来讲就是如果线程中引用的对象没有发生逃逸,则可以只将引用的对象进行分解,为其分解后的变量分配栈内存即可,无需分配堆内存空间增加GC压力。

逃逸分析的 JVM 参数如下:

  • 开启逃逸分析:-XX:+DoEscapeAnalysis
  • 关闭逃逸分析:-XX:-DoEscapeAnalysis
  • 显示分析结果:-XX:+PrintEscapeAnalysis

jdk1.6以后就默认开启了逃逸分析。

对象逃逸的几种状态

1、全局逃逸

即一个对象的作用范围逃出了当前方法或者当前线程,有以下几种场景:

  • 对象是一个静态变量
  • 对象是一个已经发生逃逸的对象
  • 对象作为当前方法的返回值
2、参数逃逸

即一个对象被作为方法参数传递或者被参数引用,但在调用过程中不会发生全局逃逸,这个状态是通过被调方法的字节码确定的。

3、没有逃逸

即方法中的对象没有发生逃逸。对象只在该方法中存活,不会逃出方法外。

逃逸分析优化

针对【没有逃逸】的状态,jvm有如下几种优化方式。

1、锁消除

因为使用同步锁,消耗性能,如果编译器通过逃逸分析可以知道当前对象只会在当前线程中使用,则会移除掉该对象的同步锁。

锁消除的 JVM 参数如下:

  • 开启锁消除:-XX:+EliminateLocks
  • 关闭锁消除:-XX:-EliminateLocks

锁消除在 JDK8 中都是默认开启的,并且锁消除都要建立在逃逸分析的基础上。

2、标量替换

首先要明白标量和聚合量,基础类型和对象的引用可以理解为标量,它们不能被进一步分解。而能被进一步分解的量就是聚合量,比如:对象。

对象是聚合量,它又可以被进一步分解成标量,将其成员变量分解为分散的变量,这就叫做标量替换。

这样,如果一个对象没有发生逃逸,那压根就不用创建它,只会在栈或者寄存器上创建它用到的成员标量,节省了内存空间,也提升了应用程序性能。

标量替换的 JVM 参数如下:

  • 开启标量替换:-XX:+EliminateAllocations
  • 关闭标量替换:-XX:-EliminateAllocations
  • 显示标量替换详情:-XX:+PrintEliminateAllocations

标量替换同样在 JDK8 中都是默认开启的,并且都要建立在逃逸分析的基础上。

3、栈上分配

当对象没有发生逃逸时,该对象就可以通过标量替换分解成成员标量分配在栈内存中,和方法的生命周期一致,随着栈帧出栈时销毁,减少了 GC 压力,提高了应用程序性能。

总结

逃逸分析,就是为了优化JVM内存和提升性能的。所以平时开发过程中,我们可以尽量控制引用变量的作用范围,让虚拟机有更多的优化空间。

比如:

return sb;

可以改为:

return sb.toString();

这样可以将StringBuilder对象的作用返回控制在当前方法中,而不会通过返回值的方式逃逸出该方法作用域。