Java中的对象都是在堆中分配吗?
2022-06-28 本文已影响0人
WilsonMing
为解答这个问题,我们要先了解对象逃逸相关内容。
什么是对象逃逸?
- 一个对象的作用域仅限于方法区域内部在使用的情况下,此种状况我们叫非对象逃逸。
- 一个对象如果被外部其他类调用,或者是作用于属性中,则此种现象被称之为对象逃逸。
对象逃逸:
/**
* sb为逃逸对象,因为sb返回出去进行使用,所以发生逃逸
*
* @param s1
* @param s2
* @return
*/
fun methodEscape(s1: String, s2: String): StringBuffer {
var sb = StringBuffer()
sb.append(s1)
sb.append(s2)
return sb
}
非对象逃逸
/**
* sb为非逃逸对象,因为方法方法返回的是一个新的String,而StringBuffer对象未产生逃逸现象
*
* @param s1
* @param s2
* @return
*/
fun method(s1: String, s2: String): String {
var sb = StringBuffer()
sb.append(s1)
sb.append(s2)
return sb.toString()
}
对于逃逸的对象他会分配到堆中,对于非逃逸的对象会在加载字节码后JIT编译优化。通过逃逸分析,如果是非逃逸对象,JIT可以有以下优化:
- 1.栈上分配:JIT编译器在编译期间根据逃逸分析计算结果,如果发现当前对象没有发生逃逸现象,那么当前对象就可能被优化分配到栈上
- 2.标量替换:有非逃逸对象可能不需要作为一个连续的内存结构存在也能被访问到, 那么对象部分可以不存储在内存,而是存储在CPU寄存器中。
标量替换
- 标量:
指一个无法在分解更小的数据的数据。在Java中的原始数据类型就是标量 - 聚合量
Java中的聚合量指的是类,封装的行为就是聚合。
标量替换其实就是把非逃逸的对象,在JIT优化分解为Java原始数据类型加载
如:
public class Paint {
int count;
int year;
public static void main(String[] args) {
Paint paint=new Paint();
}
}
中的main方法JIT优化后
public class Paint {
int count;
int year;
public static void main(String[] args) {
// Paint paint=new Paint();
int count=2;
int year=10;
}
}
逃逸分析的作用
- 代码优化
在编写代码的时候,尽可能的使用非逃逸对象方式,可以减少对象在堆上创建,非逃逸的对象创建在栈上,他会随着私有线程栈销毁就销毁。
逃逸分析的缺点
逃逸分析技术,至今还未完全成熟。原因是无法保证逃逸分析的性能消耗一定高于他的实际消耗,虽然经过逃逸分析可以做标量替换,栈上分配,锁消除等操作。但是逃逸分析自身也需要进行一系列的复杂分析算法的运算,这也是一个相对耗时过程。
Java中的对象都是在堆中分配吗?
通过的理解对象逃逸,我们在回答我们这个面试问题,答案:不是,在Java中如果对象是非逃逸对象,他们会优化分配到栈、寄存器中。