06_对象在 JVM 内存中的分配、流转
1090字约4分钟
2024-08-10
大部分对象都优先在新生代分配内存
我们再来看一下下面这个代码,理解一个概念:大部分的正常对象都是优先在新生代分配内存的
public class Order {
private static MqConsumer mqConsumer = new MqConsumer();
public static void main(String[] args) {
loadMessageFromDisk();
while (true) {
mqConsume();
}
}
public static void loadMessageFromDisk() {
MqManager mqManager = new MqManager();
mqManager.load();
}
public static void mqConsume() {
mqConsumer.consume();
}
}
Order
类静态变量 mqConsumer
引用的 MqConsumer
对象,是长期存活在内存中的,就算是这种对象,在一开始通过 new MqConsumer()
来实例化对象时,他也是分配在新生代里的
loadMessageFromDisk()
方法中创建的 MqManager
实例对象也一样,一开始分配在新生代里的
什么情况下会触发新生代的垃圾回收
假设现在 loadMessageFromDisk()
方法执行完毕之后栈帧出栈,MqManager
实例对象就没有任何局部变量引用了,此时如下图所示
那么此时就会立即发生垃圾回收,去回收掉 Java堆内存
里没有被引用的 MqManager
实例对象吗?
当然没有这么简单,实际上垃圾回收是有触发条件的
有一个比较常见的场景,我们写代码中创建了很多对象,在 Java堆内存
中囤积了大量的对象,这些对象之前有被引用,比如各种各样方法中的局部变量,但是现在都没有被引用了,如下图所示
如果我们预分配的新生代内存空间被对象占满了,继续在新生代里再去分配一个对象,怎么办?
这个时候,就会触发一次新生代内存空间的垃圾回收,垃圾回收线程会尝试把新生代里那些没有被引用的垃圾对象给回收掉,比如 MqManager
实例对象就是没有被引用的垃圾对象,它就会被回收掉,然后释放出内存空间,将新的对象分配进去
新生代的垃圾回收被称为 Minor GC
,也称作 Young GC
也就是说,当垃圾回收线程发现新生代内存空间不足,就会触发一次垃圾回收,将垃圾对象回收掉,腾出大量的内存空间
长期存活的对象会躲过多次垃圾回收
像 MqConsumer
实例对象,是一个被 Order
类的静态变量 mqConsumer
引用而长期存活的对象
虽然新生代可能随着系统的运行,不停的创建对象,让新生代内存空间变满,接着触发一次垃圾回收,大量对象被回收掉
但是 MqConsumer
这个对象一直被 Order
类的静态变量引用着,不会回收,那么 JVM
中就有一条规定
如果一个实例对象在新生代中,在 15
次垃圾回收之后都没有被回收掉,说明这个对象的年龄已经 15
岁了(躲过一次回收年龄加一)
所以 MqConsumer
对象会在新生代成功躲过十多次垃圾回收之后,成为了一个“老年人”,被认为是会长期存活在内存里的对象,然后就会被转移到 Java堆内存
的老年代中去,顾名思义,老年代就是放这些年龄很大的对象
关于新生代和老年代的其他内容
在对象分配这块,还有很多其他的复杂机制,后续再详细探讨,比如
新生代垃圾回收之后,因为存活对象太多,导致大量对象直接进入老年代
特别大的对象直接不经过新生代就进入老年代
动态对象年龄判断机制
空间担保机制
老年代会垃圾回收吗