网站首页 > java教程 正文
前言
说到JDK1.8的垃圾回收,想必大家都可以在脑海中浮现如下一张流程图。
上述流程图示意了JDK1.8在默认情况下也就是Parallel Scavenge + Serial Old(PS MarkSweep) 垃圾收集器组合下的一个大致垃圾回收过程。但是如果是大对象的话,实际垃圾回收可能和上面的流程还稍有区别,具体的区别在于如下两点。
- ParNew或者SerialOld垃圾收集器下,使用-XX:PretenureSizeThreshold参数指定一个阈值,大小大于指定阈值的对象将直接进入老年代;
- 如果大对象是一个在新生代分配失败且不包含任何对象引用的数组,则直接进入老年代。
上述第一点想必大家是背烂了的,但是第二点应该稍有陌生。针对第二点,在《Frequently Asked Questions about Garbage Collection in the HotspotTM JavaTM Virtual Machine》一文中有如下一段话进行阐述。
If an allocation fails in the young generation and the object is a large array that does not contain any references to objects, it can be allocated directly into the old generation. In some select instances, this strategy was intended to avoid a collection of the young generation by allocating from the old generation.
那么本文就针对上述的这种不包含任何对象引用的数组的垃圾回收进行一个探究。
正文
首先提供如下两个方法来创建本文讨论的不包含任何对象引用的数组这种大对象。
java复制代码private final List<Byte[]> bytesList = new ArrayList<>();
public void addMemory() {
Byte[] bytes = new Byte[1024 * 1024 * 50];
bytesList.add(bytes);
}
java复制代码public void addMemoryAllowGc() {
Byte[] bytes = new Byte[1024 * 1024 * 50];
}
区别在于第一个方法创建的大对象无法被GC,而第二个方法创建的大对象可以被GC,此外两个方法每次创建的对象占用内存大约为200MB。
把程序运行起来时,初始状态下内存使用率和GC次数如下所示。
此时eden区的内存使用近乎是满的状态,而old区近乎没有被使用。现在调用addMemoryAllowGc() 方法,创建可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
可见直接在old区完成了大对象创建。继续调用addMemoryAllowGc() 方法,创建可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
继续调用addMemoryAllowGc() 方法,创建可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
此时eden区和old区都无法再分配出200MB的内存空间,现在再调用一次addMemoryAllowGc() 方法,创建可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
我们在eden区和old区剩余内存都不足200MB的情况下,又想要创建一个200MB的大对象,首先会尝试在eden区创建,发现eden区剩余内存不足以创建,然后又会尝试在old区创建,发现old区剩余内存也不足以创建,此时就会执行一次Young GC(上图中Young GC次数加了1),Young GC完毕后,会再尝试在eden区创建对象,因为Young GC完毕后eden区已经有足够的内存空间了,所以我们本次创建的大对象就创建在了eden区。现在继续调用addMemory() 方法创建不可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
还是同样的道理,eden区和old区都没有足够的空间来创建对象,所以先执行一次Young GC(上图中Young GC次数加了1),然后再在eden区创建对象,但是注意,最终eden区创建出来的200MB的大对象是无法被GC的(对应的Byte数组对象存在强引用)。现在再调用一次addMemoryAllowGc() 方法,创建可以被GC的200MB的大对象,内存使用率和GC次数如下所示。
可见最终Young GC和Old GC次数均加1,这是因为一开始eden区和old区都没有足够的空间来创建对象,所以先执行了一次Young GC(上图中Young GC次数加了1),但是eden区此时是有一个被强引用的大对象占据了约200MB的内存空间,这个大对象在Young GC后会存活下来,但此时survivor区无法容纳这个大对象,所以这个大对象直接进入old区,可old区此时也无法容纳这个大对象,最终就触发了Old GC,之后old区就有了足够的内存空间容纳这个大对象,再之后新创建的对象就在eden区完成创建。这个过程也可以看作是发生了一次Full GC,因为Young GC和Old GC均发生了一次。
总结
现在对不包含任何对象引用的大数组这样的对象的GC的一个具体流程进行总结。
- 新对象优先在eden区创建;
- 新对象如果过大,导致eden区分配内存失败,则新对象直接在old区创建;
- 如果old区也无法容纳新对象,则触发Young GC;
- Young GC时存活下来的对象会进入survivor区,如果survivor区无法容纳,则存活下来的对象直接进入old区,如果old区无法容纳,则触发Old GC。
猜你喜欢
- 2025-06-12 JVM_什么是G1垃圾收集器#java编程
- 2025-06-12 Java11 的 G1 垃圾收集器(java垃圾收集器的种类)
- 2025-06-12 面试Java被问JVM:你来详细聊一下G1垃圾收集器
- 2025-06-12 面试官:什么是垃圾回收?-(垃圾回收系列:1)
- 2025-06-12 Java性能优化要点之:GC垃圾回收知识点整理
- 2025-06-12 深入浅出JVM之垃圾回收算法(jvm垃圾回收算法和垃圾回收器)
- 2025-06-12 JVM(Java虚拟机)内存结构和垃圾回收(GC)机制
- 2025-06-12 Java 为什么不采用 360 垃圾清理来进行垃圾回收呢?
- 2025-06-12 Java面试终极:分布式垃圾回收(DGC)答错必挂!
- 2025-06-12 JVM03——四种垃圾回收算法,你都了解了哪几种
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)