赞
踩
【面试精讲】Java有哪些垃圾回收器?工作原理都是什么?它们有什么区别?
目录
在Java世界中,垃圾回收(Garbage Collection, GC)是自动内存管理的一部分,它帮助开发者免于直接处理内存分配和释放,从而避免了许多内存泄漏和指针错误。随着Java技术的演进,出现了多种垃圾回收器,它们各有特点,适用于不同的场景和需求。
本文将深入探讨Java中的Serial、Parallel Scavenge、CMS、G1主要垃圾回收器,它们的工作原理以及它们之间的区别。
其中 CMS 收集器是 JDK 8 之前的主流收集器, JDK 9 之后的默认收集器为 G1
在Java虚拟机(JVM)中每种回收器都有其独特的工作原理和使用场景,主要的垃圾回收器包括:
- 串行垃圾回收器(Serial GC):这是最基本的GC实现,它在进行垃圾回收时会暂停所有应用线程("Stop-The-World"),因此它适用于单核服务器或者用于客户端应用中。
- 并行垃圾回收器(Parallel GC):并行回收器在垃圾收集阶段同样会暂停所有应用线程,但它在垃圾回收时使用多个线程并行工作,因此在多核服务器上性能较好。它是JVM的默认垃圾回收器。
- 并发标记清除(CMS)垃圾回收器:CMS回收器的目标是减少应用暂停时间。它在回收大部分垃圾时应用线程可以继续工作。它的缺点是会产生较多的内存碎片。
- G1垃圾回收器:G1是一个面向服务器的垃圾回收器,旨在在具有大量内存的多核机器上,实现高吞吐量和低暂停时间。它通过将堆划分为多个区域(Region)来实现。
- ZGC(Z Garbage Collector):这两个是最新的垃圾回收器,旨在实现几乎没有暂停时间的垃圾收集。它们适用于需要极低暂停时间及大堆内存的应用。
使用单个线程进行垃圾回收,它在进行垃圾回收时会停止所有用户线程,直到垃圾回收完成。
并行回收器在进行垃圾收集时,多个垃圾收集线程并行工作,以提高垃圾收集的效率。它主要用于在垃圾收集时减少系统暂停的时间。
CMS回收器分为几个阶段,其中大部分阶段都可以与应用线程同时运行,只有在初始标记和重新标记阶段才需要暂停所有应用线程。
G1回收器通过将堆内存划分为多个区域,并在这些区域中进行增量式的垃圾回收来实现高效的垃圾回收。它旨在提供一种更灵活的垃圾回收方式,以达到低暂停时间和高吞吐量的平衡。
选择哪种垃圾回收器取决于应用的需求:
对于需要低延迟的应用,可以考虑使用CMS、G1或ZGC和Shenandoah。
如果应用运行在单核或者内存较小的环境中,串行回收器Serial GC可能是最佳选择。
对于追求吞吐量的应用,可以考虑使用并行回收器Parallel GC或G1回收器。
串行垃圾回收器(Serial GC)是Java中最古老且简单的垃圾回收器之一,它针对单线程环境设计,适用于小型数据处理和具有较小内存的JVM实例。由于它是单线程工作的,因此串行GC在执行垃圾收集时会暂停所有应用线程,这种暂停被称为“Stop-The-World”(STW)事件。
串行垃圾回收器在年轻代采用标记-复制
(Mark-Copy)算法,在老年代采用标记-整理
(Mark-Sweep-Compact)算法。这两种算法的基本概念如下:
- public class SerialGCSimulator {
- // 模拟堆内存结构
- private static class Heap {
- Object[] youngGen; // 年轻代
- Object[] oldGen; // 老年代
-
- public Heap(int youngSize, int oldSize) {
- youngGen = new Object[youngSize];
- oldGen = new Object[oldSize];
- }
-
- // ... 其他堆操作方法 ...
- }
-
- // 标记过程
- private void mark(Object obj, boolean[] reachable) {
- // 假设通过某种方式可以获取对象引用,并标记为可达
- // 在实际的JVM中,这一过程会从根集合开始,遍历所有可达对象
- if (obj != null) {
- int index = findIndexOf(obj);
- reachable[index] = true;
- }
- }
-
- // 复制过程
- private void copy(boolean[] reachable, Object[] fromSpace, Object[] toSpace) {
- int toIndex = 0;
- for (int i = 0; i < fromSpace.length; i++) {
- if (reachable[i]) {
- toSpace[toIndex++] = fromSpace[i]; // 复制可达对象
- fromSpace[i] = null; // 清除原位置上的对象引用
- }
- }
- }
-
- // 整理过程
- private void compact(Object[] generation) {
- int toIndex = 0;
- for (Object obj : generation) {
- if (obj != null) {
- generation[toIndex++] = obj; // 移动对象,压缩空间
- }
- }
- // 清除剩余的垃圾对象
- for (int i = toIndex; i < generation.length; i++) {
- generation[i] = null;
- }
- }
-
- // 运行垃圾回收模拟
- public void runGC(Heap heap) {
- // 假设知道每个对象是否可达
- boolean[] youngReachable = new boolean[heap.youngGen.length];
- boolean[] oldReachable = new boolean[heap.oldGen.length];
-
- // 模拟标记过程
- for (Object obj : heap.youngGen) {
- mark(obj, youngReachable);
- }
- for (Object obj : heap.oldGen) {
- mark(obj, oldReachable);
- }
-
- // 模拟复制过程(年轻代)
- Object[] newYoungGen = new Object[heap.youngGen.length];
- copy(youngReachable, heap.youngGen, newYoungGen);
- heap.youngGen = newYoungGen;
-
- // 模拟整理过程(老年代)
- compact(heap.oldGen);
-
- // 垃圾回收完成
- }
-
- private int findIndexOf(Object obj) {
- // 实现省略...
- return 0;
- }
-
- // 主函数,运行垃圾回收模拟
- public static void main(String[] args) {
- Heap heap = new Heap(256, 1024); // 创建一个假设的堆
- SerialGCSimulator gcSimulator = new SerialGCSimulator();
- gcSimulator.runGC(heap); // 执行一次垃圾回收
- }
- }
并行垃圾回收器也称为吞吐量优先回收器,它使用多个线程来缩短垃圾回收的停顿时间。这种回收器特别适合多CPU环境,因为它能够并行利用多个CPU核心完成垃圾回收工作,以提高应用程序的吞吐量。
并行GC在不同代区采用的算法如下:
- public class ParallelGCSimulator {
- // 模拟堆内存结构
- private static class Heap {
- Object[] youngGen; // 年轻代
- Object[] oldGen; // 老年代
-
- public Heap(int youngSize, int oldSize) {
- youngGen = new Object[youngSize];
- oldGen = new Object[oldSize];
- }
-
- // 标记过程
- private void mark(Object obj, boolean[] reachable) {
- // 省略具体实现,仅模拟标记对象
- if (obj != null) {
- int index = findIndexOf(obj);
- reachable[index] = true;
- }
- }
-
- // 复制过程
- private void copy(boolean[] reachable, Object[] fromSpace, Object[] toSpace) {
- int toIndex = 0;
- for (int i = 0; i < fromSpace.length; i++) {
- if (reachable[i]) {
- toSpace[toIndex++] = fromSpace[i]; // 复制可达对象
- fromSpace[i] = null; // 清除原位置上的对象引用
- }
- }
- }
-
- // 压缩过程(老年代)
- private void compact(Object[] generation) {
- // 省略具体实现,仅模拟压缩过程
- }
-
- // 运行并行垃圾回收模拟
- public void runGC() {
- // 年轻代使用标记-复制算法
- // 假设知道每个对象是否可达
- boolean[] youngReachable = new boolean[youngGen.length];
- for (Object obj : youngGen) {
- mark(obj, youngReachable);
- }
- Object[] newYoungGen = new Object[youngGen.length];
- copy(youngReachable, youngGen, newYoungGen);
- youngGen = newYoungGen;
-
- // 老年代使用标记-压缩算法
- boolean[] oldReachable = new boolean[oldGen.length];
- for (Object obj : oldGen) {
- mark(obj, oldReachable);
- }
- compact(oldGen);
- }
-
- private int findIndexOf(Object obj) {
- // 省略具体实现...
- return 0;
- }
- }
-
- public static void main(String[] args) {
- Heap heap = new Heap(256, 1024); // 创建一个假设的堆
- heap.runGC(); // 执行一次垃圾回收,模拟并行GC工作
- }
- }
CMS垃圾回收器主要目标是获取最短回收停顿时间,通常用于互联网公司或者用户界面较为丰富的应用程序中。CMS回收器试图尽可能减少应用程序的停顿时间,特别是对老年代的垃圾回收进行了优化。
CMS GC的工作过程分为以下四个主要阶段:
- public class CMSimulator {
- // 堆内存的简单表示
- private Object[] oldGen; // 老年代
-
- // 构造方法
- public CMSimulator(int size) {
- oldGen = new Object[size];
- }
-
- // 初始标记
- private void initialMark() {
- // STW事件,快速扫描GC Roots直接引用的对象
- // 省略实现...
- }
-
- // 并发标记
- private void concurrentMark() {
- // 应用线程可以并发运行
- // 遍历对象图,标记所有可达对象
- // 省略实现...
- }
-
- // 重新标记
- private void remark() {
- // STW事件,通常使用三色标记和写屏障技术来优化
- // 省略实现...
- }
-
- // 并发清除
- private void concurrentSweep() {
- // 应用线程可以并发运行
- // 清理未标记(即不可达)的对象
- // 省略实现...
- }
-
- // 执行CMS垃圾回收
- public void runCMS() {
- initialMark();
- concurrentMark();
- remark();
- concurrentSweep();
- }
-
- public static void main(String[] args) {
- CMSimulator cmSimulator = new CMSimulator(1024); // 创建CMS模拟器
- cmSimulator.runCMS(); // 开始执行CMS垃圾回收
- }
- }
G1垃圾回收器是一种服务器端的垃圾回收器,旨在兼顾高吞吐量与低延迟。它通过划分内存为多个相同大小的区域(Region),尝试以增量方式来处理这些区域,从而最大限度减少单次垃圾收集的停顿时间。
G1回收器主要分为以下阶段:
- public class G1GCSimulator {
- // 模拟Heap的Region划分
- private static class Heap {
- List<Object[]> regions;
-
- public Heap(int regionCount, int regionSize) {
- regions = new ArrayList<>(regionCount);
- for (int i = 0; i < regionCount; i++) {
- regions.add(new Object[regionSize]);
- }
- }
-
- // ... 其他Heap操作方法 ...
- }
-
- // 标记过程和G1中复杂的回收逻辑在模拟代码中无法完全体现
- // 下面是简化的演示过程
-
- // 初始标记
- private void initialMark(Heap heap) {
- // STW事件,标记直接可达对象
- // ... 标记逻辑 ...
- }
-
- // 并发标记
- private void concurrentMark(Heap heap) {
- // 应用线程并发执行,G1遍历对象图
- // ... 标记逻辑 ...
- }
-
- // 最终标记
- private void finalMark(Heap heap) {
- // STW事件,处理变化的对象引用
- // ... 标记逻辑 ...
- }
-
- // 筛选回收
- private void evacuation(Heap heap) {
- // STW事件,选择部分Region进行回收
- for (Object[] region : heap.regions) {
- // 假设有一个方法来决定是否需要回收这个Region
- if (shouldCollect(region)) {
- // 回收并移动对象到其他Region
- for (int i = 0; i < region.length; i++) {
- if (isMarked(region[i])) {
- moveToNewRegion(region[i]);
- }
- region[i] = null; // 回收对象
- }
- }
- }
- }
-
- // 运行G1垃圾回收模拟
- public void runGC(Heap heap) {
- initialMark(heap); // 初始标记
- concurrentMark(heap); // 并发标记
Java的垃圾回收器提供了多种选择,以满足不同应用的性能和延迟需求。理解每种垃圾回收器的工作原理和特点,可以帮助开发者为他们的应用选择最合适的垃圾回收策略,优化应用性能,提升用户体验。
如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续创作!!!
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/450747
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。