内存逃逸
方法逃逸
-
概念:一个方法内部创建的对象被外部方法引用(比如将对象作为返回值传递到其它方法中),我们称之为方法逃逸。
线程逃逸
- 概念:一个线程内部创建的对象被外部线程引用(比如将对象作为返回值传递到其它线程中),我们称之为线程逃逸。
内存逃逸分析:
概念:
- 通过动态分析对象的作用域,分析某些对象是否存在方法逃逸或线程逃逸,为其它优化手段(如栈上分配、同步消除和标量替换等)提供依据。
作用:
- 若jvm开启了逃逸分析,JIT会对代码进行如下优化:
栈上分配对象:
- 若某个对象不存在方法逃逸,那么这个对象就可以直接分配在栈中,之后该对象会随着栈帧出栈(方法执行结束后)而销毁,减轻了gc的压力。
同步消除:
- 锁消除:若某个对象不存在线程逃逸,那么这个对象的读写就不会存在竞争,所以访问该对象的同步锁就可以被清除掉了。
- 锁粗化:JVM 针对那些反复在一段代码中对同一对象加锁的情况,将同步锁放在最外层包住这里面的多次同步锁,同时取消内部的同步锁。
标量替换:
- 标量:基础数据类型(int,char,long等)的变量 和 Reference类型变量(SoftReference等) 不能进行分解,这些不能分解的变量称为标量。
- 聚合量:若一个对象可以继续分解,那么就称这个对象为聚合量。
- 若逃逸分析证明一个对象不会逃逸出方法,不会被外部访问,并且这个对象是可以被分解的,那程序在真正执行的时候可能不创建这个对象,而是直接创建这个对象分解后的标量来代替。这样就无需在对对象分配空间了,只在栈上为分解出的变量分配内存即可。
缺点:
- 逃逸分析是比较耗时的,所以性能未必提升很多,因为其耗时性,采用的算法都是不那么准确但是时间压力相对较小的算法来完成的,这就可能导致效果不稳定。
参数
逃逸分析参数:
- -XX:+DoEscapeAnalysis
- 开启逃逸分析,从jdk7开始默认开启。
- -XX:-DoEscapeAnalysis
- 关闭逃逸分析
- -XX:+PrintEscapeAnalysis
- 开启逃逸分析后,可通过此参数查看分析结果。
锁消除:
- -XX:+EliminateLocks
- 开启锁消除,依赖-XX:+DoEscapeAnalysis参数,jdk8默认开启。
- -XX:-EliminateLocks
- 关闭锁消除
标量替换:
- -XX:+EliminateAllocations
- 开启标量替换,依赖-XX:+DoEscapeAnalysis参数,jdk8默认开启。
- -XX:+PrintEliminateAllocations
- 开启标量替换后,查看标量替换情况