JVM : 7 对象在JVM内存中如何分配与流转?
Posted 鮀城小帅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM : 7 对象在JVM内存中如何分配与流转?相关的知识,希望对你有一定的参考价值。
1. 两种对象
代码里创建出来的对象,一般有如下两种:
- 一种是短期存活的,分配在 Java 堆内存之后,迅速使用完就会被垃圾回收;
- 另外一种是长期存活的,需要一直生存在 Java 堆内存里,让程序后续不停的去使用
前者的对象,是在 Java 堆内存的新生代里;而后者的对象,是在 Java 堆内存的老年代里。
2. 大部分正常对象都优先在新生代分配内存
问题:对象肾情况下短期存活?
要知道,大部分正常对象,都优先在新生代分配内存的。
按上一章所说,类静态变量 "fetcher" 引用的 “ReplicaFetcher” 对象,是会长期存活在内存里的。但是,它在开始通过 ”new ReplicaFetcher()“ 代码来实例化一个对象时,也是先分配在新生代里的。
包括在 “loadReplicasFromDisk()” 方法中创建 “ReplicaManager”实例对象,也是先分配在新生代里的。
如下图:
3. 什么情况下会触发新生代的垃圾回收?
在 “loadReplicasFromDisk()” 方法执行完毕后,该方法的栈帧出栈,会导致没有任何局部变量引用那个 “ReplicaManager” 实例对象。
在没有局部变量引用实例对象的情况下,不会立即发生垃圾回收。因为垃圾回收是有触发的条件的。
触发垃圾回收的场景:
当程序创建了N多对象,然后导致 Java堆内存里囤积了大量的对象。在新生代的内存空间几乎被全部对象占满的情况下,如果要给新的对象分配内存,此时发现新生代里内存空间不够用。
而此时新生代中有部分对象的局部变量没有人引用。就会触发一次新生代内存空间的垃圾回收,也就是 “Minor GC”,也叫 “Young GC” ,这是常识把新生代里那些没有人引用的垃圾对象,都给回收掉。
在触发一次垃圾回收,就会把所有垃圾对象给干掉,从而腾出大量的内存空间。如下图:
4. 长期存活的对象会躲过多次垃圾回收
如 “ReplicaFetcher” 实例对象这种长期被 “Kafka” 类的静态变量“fetcher” 引用的长期存活对象。在新生代进行垃圾回收时,会一直存活,而不被回收。
JVM 中有一条规定,如果一个实例对象在新生代中,成功的在 15 次垃圾回收之后,还是没被回收掉,就说明它已经15岁了。(每经历一次垃圾回收增加1岁)
如示例中的 “ReplicaFetcher” 对象,它在新生代如果成功躲过 15次垃圾回收,就会被认为是会长期存活在内存里的对象。
从而被转移到 Java 堆内存的老年代中去。如下图:
5. 老年代会垃圾回收吗?
老年代里的对象也是会被垃圾回收的。因为老年代的对象也有可能随着代码的运行,不再被引用到,就需要被垃圾回收。
6. 关于新生代和老年代的对象分配,还存在哪些机制
新生代和老年代,在对象分配方面,还有很多的复杂机制,比如:
- 新生代垃圾回收之后,因为存活对象太多,导致大量对象直接进入老年代;
- 特别大的超大对象直接不经过新生代就进入老年代;
- 动态对象年龄判断机制;
- 空间担保机制
小小的总结:
- 理解对象优先分配在新生代;
- 新生代如果对象满了,会触发 Minor GC 回收掉没有人引用的垃圾对象;
- 如果有对象躲过了十多次垃圾回收,就会放入老年代里;
- 如果老年代也满了,那么也会触发垃圾回收,把老年代里没人引用的垃圾对象清理掉。
7.思考题
每个线程都有 Java 虚拟机栈,里面也有方法的局部变量等数据,这个 Java虚拟机栈需要进行垃圾回收吗?为什么?
答:JVM里垃圾回收针对的是新生代,老年代,还有方法区(永久代),不会针对方法的栈帧。方法一旦执行完毕,栈帧出栈,里面的局部变量直接就从内存里清理掉了。
以上是关于JVM : 7 对象在JVM内存中如何分配与流转?的主要内容,如果未能解决你的问题,请参考以下文章