对象分配
Java对象一定是在堆上分配的吗?
绝大多数的对象是在堆上分配的,但是也有例外。随着JIT即时编译器的出现,内存逃逸分析愈发成熟,栈上分配,标量替换,锁消除等优化技术也慢慢发展起来。
Java是通过javac编译成字节码,然后类加载器将该字节码文件加载到内存,逐行解析并翻译成机器码执行,因此解释执行会稍微慢一点。
而JIT会将频繁运行的代码定义为热点代码,将之编译成机器指令,加快运行。
内存逃逸分析是JIT重要的优化技术。
定义
一个对象是否会逃逸,主要看一个对象在一个方法内定义,是否有可能被外部引用了。
如:
public Test use(){
Test test = new Test();
return test;
}
这种情况下,会发生逃逸。
方法逃逸:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中
线程逃逸:甚至该对象还可能被外部线程访问到,例如赋值被类变量或可以在其他线程中访问的实例变量
进行内存逃逸分析的目的是为了进一步优化程序。
主要从:
- 栈上分配
- 标量替换
- 锁消除
栈上分配
如果一个对象没有发生内存逃逸,那该对象是有可能在栈上进行分配的,而不用分配到堆上,减少GC。
由于复杂度等原因,HotSpot中目前暂时还没有做这项优化,但一些其他的虚拟机(如Excelsior JET)使用了该优化。
标量替换
如果一个对象没有发生内存逃逸,那么就有可能将该对象分解成若干个标量来代替。
标量替换可视为栈上分配一种特例,实现更简单(不用考虑对象完整结构的分配),但对逃逸程度的要求更高,它不允许对象逃逸出方法范围内。
public void replace() {
Test test= new Test (1, 2);
System.out.println(test.a);
System.out.println(test.b);
}
————————————————
```java
public void replace() {
Test test= new Test (1, 2);
System.out.println(1);
System.out.println(2);
}
在虚拟机中,并没有真正的实现栈上分配,本质上都是通过标量替换来实现
锁消除
如果一个对象没有发生内存逃逸,只能当前线程访问到,那么可能会进行优化锁消除。
开启
从Java1.7开始就默认开启了内存逃逸分析。
# 开启
-XX:+DoEscapeAnalysis
# 关闭
-XX:-DoEscapeAnalysis