标量替换
首先要明确数据分为聚合量和标量,标量是不可在继续分的,而聚合量是能够继续分的。如果逃逸分析证明一个对象不会被外访问或者引用,并且这个对象是可以分的,那么创建的时候就不会创建这个对象了,而改为使用若干个标量来替换它,这样是为了拆开以后便于标量可以在各个栈上进行分配了。
进行逃逸分析
//参数如下 -server -Xms100m -Xmx100m -XX:+UseParallelGC -XX:-DoEscapeAnalysis -XX:+UseTLAB -XX:+EliminateAllocations
public class EscapeAnalysisTest {
static class User{
byte[] bytes = new byte[1024*20];
}
public static void alloc(){
User user = new User();
// byte[] bytes = new byte[1024*20];这里首先关闭
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(int i = 0;i<100000;i++){
alloc();
}
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime+"ms");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
因为User是一个聚合量,所以执行的时候如果没有开启逃逸分析,就不会再栈上分配了,而改为在堆中分配。结果如下
然后开启逃逸分析 -XX:+DoEscapeAnalysis
执行结果如下
发现时间减少了
如果关闭标量替换 -XX:-EliminateAllocations会发现时间又变回来原来的关闭逃逸分析的时候了,所以可以证明这个对象是使用标量替换进行栈上分配的
假如把User换成 byte[]
//参数如下 -server -Xms100m -Xmx100m -XX:+UseParallelGC -XX:-DoEscapeAnalysis -XX:+UseTLAB -XX:+EliminateAllocations
public class EscapeAnalysisTest {
static class User{
byte[] bytes = new byte[1024*20];
}
public static void alloc(){
// User user = new User();
byte[] bytes = new byte[1024*20];
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(int i = 0;i<100000;i++){
alloc();
}
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime+"ms");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果如下
关闭逃逸分析 -XX:-DoEscapeAnalysis会发现还是一样的结果
因此对于不可再分的标量,其直接在栈上分配。