正常情况下,Java对象是存储到堆上的,为了优化系统性能,Java虚拟体提供了一种栈上分配的技术,栈上分配的基本思想是:对于那些线程私有的对象(不能被其他线程访问的对象),Java虚拟机将对象打散分配到栈上,而不是分配到堆上。
之所以分配到栈上,是因为在函数调用结束后,可以将对象信息自行销毁,而不需要垃圾回收器介入。
栈上分配技术基础是逃逸分析,逃逸分析的目的是检查对象的作用域是否有可能逃出函数体。
能够逃逸的对象
下面演示一个能够逃逸的对象,因为能够逃逸,所以是无法进行栈上分配的。
public class Test{
public static class User{
public String name;
public int age;
public User(String name,int age){
this.name=name;
this.age=age;
}
}
public static User alloc(){
User user = new User("Test",30);
user.name = "张三";
user.age=40;
return user;
}
public static void main(String[] args) throws InterruptedException{
long startTime = System.currentTimeMillis();
User user = new User("Test",30);
for(int i=0;i<100000000;i++){
user= alloc();
}
System.out.println(user.age);
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
执行
java -server -Xmx20m -Xms20m -XX:+PrintGC Test
可以看到代码发生了大量的GC。
不能逃逸的对象
相同的代码,我们只是设置对象不能逃逸,进行一下改造。
public class Test{
public static class User{
public String name;
public int age;
public User(String name,int age){
this.name=name;
this.age=age;
}
}
public static void alloc(){
User user = new User("Test",30);
user.name = "张三";
user.age=40;
}
public static void main(String[] args) throws InterruptedException{
long startTime = System.currentTimeMillis();
for(int i=0;i<100000000;i++){
alloc();
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
执行
java -server -Xmx20m -Xms20m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations Test
输出
可以看到并没有任何GC程序就执行完了。
参数说明
第二段启用逃逸分析时,我们参数明显是比第一段多了。-server
:Server模式下,才可以启用逃逸分析。-XX:+DoEscapeAnalysis
: 启用逃逸分析。-XX:-UseTLAB
:TLAB全称是Thread Local Allocation Buffer,也就是线程级别的内存分配,每个线程独享一块内存,主要是针对Eden
里的一块小内存,这样线程在分配内存的时候,因为这块小内存是这个线程独享的,因此不会出现锁竞争的问题,默认是开启该功能的,不建议关闭此功能,主要是保证内存分配的高效性-XX:+EliminateAllocations
:开启标量替换(默认开启),运行将对象打散分配到栈上,比如我们上面User
对象有id
、name
两个字段,那么这两个字段将会被视为两个独立的局部变量进行分配。
评论 (0)