- HotSpot逃逸分析
逃逸分析并不是直接的优化手段,它是通过动态分析对象的作用域,为其它优化手段如栈上分配、标量替换和同步消除等提供依据,发生逃逸行为的情况有两种:方法逃逸、线程逃逸。
- 方法逃逸:当一个对象在方法中定义之后,作为参数传递到其它方法中;
- 线程逃逸:如类变量或实例变量,被其它线程访问到;
如果不存在逃逸行为,则可以对该对象进行如下优化:同步消除、标量替换和栈上分配。
- 同步消除
线程同步本身比较耗时,如果确定一个对象不会逃逸出线程,无法被其它线程访问到,那该对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks可以开启同步消除。
- 标量替换
- 标量是指不可分割的量,如Java中基本数据类型和reference类型,相对的是一个数据可以继续分解,称为聚合量;
- 如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换;
- 如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干个成员变量;通过-XX:+EliminateAllocations可以开启标量替换, -XX:+PrintEliminateAllocations查看标量替换情况。
- 栈上分配
故名思议就是在栈上分配对象,其实目前Hotspot并没有实现真正意义上的栈上分配,实际上是标量替换。
分层编译和逃逸分析在1.8中是默认是开启的,关闭了分层编译之后,在Java堆上分配的User对象降低到1w多个,分层编译对逃逸分析还是有影响的。
即时编译JIT只在代码段执行足够次数才会进行优化,在执行过程中不断收集各种数据,作为优化的决策,所以在优化完成之前,例子中的User对象还是在堆上进行分配。
那么一段代码需要执行多少次才会触发JIT优化呢?通常这个值由-XX:CompileThreshold参数进行设置:
1、使用client编译器时,默认为1500;
2、使用server编译器时,默认为10000;
意味着如果方法调用次数或循环次数达到这个阈值就会触发标准编译,更改CompileThreshold标志的值,将使编译器提早(或延迟)编译。