Java虚拟机(JVM)的垃圾回收算法有多种,每种算法都有其独特的特点和适用场景。以下是一些常见的垃圾回收算法:
- 标记-清除算法(Mark and Sweep):
- 标记阶段: 从根对象出发,标记所有可以被访问到的对象。
- 清除阶段: 清除未被标记的对象,释放其内存。
标记-清除算法是一种基础的垃圾回收算法,分为两个阶段:标记阶段和清除阶段。
1. 标记阶段:
在标记阶段,垃圾收集器从根对象出发,遍历对象之间的引用关系,标记所有可以被访问到的对象。根对象通常是程序中已经存在的引用,如静态变量、方法区中的常量等。
具体步骤:
-
从根对象开始: 垃圾收集器从根对象出发,追踪其引用的对象,然后递归地遍历引用链,标记所有可达对象。
-
标记过程: 对于每个可达对象,垃圾收集器会标记它,通常通过在对象头部添加标记位或维护一个标记表来记录已标记的对象。
-
继续遍历: 垃圾收集器会继续遍历引用链,标记所有可达对象,直到无法继续追踪到新的对象。
2. 清除阶段:
在清除阶段,垃圾收集器会清除未被标记的对象,释放其占用的内存空间。这些未被标记的对象即为不可达对象,它们不再被程序引用,也就不再被使用。
具体步骤:
-
遍历整个堆: 垃圾收集器会遍历整个堆内存,检查每个对象的标记状态。
-
清除未标记的对象: 对于未被标记的对象,垃圾收集器将其内存释放,归还给堆内存池,以便下次分配新的对象时使用。
-
回收空间: 清除后的内存空间被重新标记为空闲,可以用于新的对象的分配。
特点:
-
效率低: 标记-清除算法的主要问题是会产生内存碎片,因为清除阶段释放的内存不一定是连续的,导致难以找到足够大的连续空间来分配大对象。
-
停顿时间: 清除阶段可能会引起较长的停顿时间,因为在清除期间,整个应用程序可能需要被暂停。
虽然标记-清除算法有一些缺点,但它为后续的垃圾回收算法提供了基础,并且在理论上具有简单性和可行性。实际上,很多现代的垃圾回收算法对标记-清除算法进行了优化和改进,以提高效率和降低停顿时间。
2. 复制算法(Copying):
- 将内存分为两块,每次只使用其中一块。
- 在垃圾回收时,将存活的对象复制到另一块内存中,然后清空当前内存。
- 优点是简单高效,但空间利用率较低。
-
标记-整理算法(Mark and Compact):
- 类似于标记-清除,但在标记后会将存活的对象整理在一起,减少碎片化。
- 对象移动可能引起引用更新。
-
分代垃圾回收算法(Generational Garbage Collection):
- 将堆分为年轻代(Young Generation)和老年代(Old Generation)。
- 大部分对象在年轻代中被快速回收,只有存活较长时间的对象会被移到老年代。
- 可采用不同的垃圾回收算法针对不同代进行优化。
-
并发标记-清除算法(Concurrent Mark and Sweep):
- 在标记和清除的过程中,允许程序的线程和垃圾回收线程并发执行,减少停顿时间。
- 适用于需要低停顿时间的场景。
-
G1收集器(Garbage-First Garbage Collector):
- 将堆划分为多个区域,通过并发标记-整理算法实现垃圾回收。
- 优化了停顿时间和吞吐量的平衡,适用于大堆和低延迟要求的场景。
-
ZGC(Z Garbage Collector):
- 采用了某些与G1相似的区域划分和回收策略。
- 通过着色指针和并发处理来减少停顿时间,适用于对低延迟有高要求的应用。
-
Shenandoah收集器:
- 采用了类似G1的分代思想,同时借鉴了ZGC的一些技术。
- 主要目标是降低垃圾回收的停顿时间。
选择哪种垃圾回收算法取决于应用的性质和需求。不同的垃圾回收算法在吞吐量、停顿时间、内存占用等方面有不同的权衡。 Java虚拟机通常会根据应用的特点和运行时的情况来动态选择合适的垃圾回收策略。