# Jvm中gc的算法演进

垃圾回收是JVM的一大功能模块，面试中经常会被问到有哪些垃圾回收算法，那么这些垃圾回收算法是如何被设计出来的呢？首先要回到根本的问题上来。

### **问题的起源：为何需要垃圾回收**

在早期编程语言中，程序员需要手动管理内存：分配内存并在不需要时释放。这一过程容易出错，导致内存泄漏（未被释放的内存）或悬挂指针（指向已释放内存的指针），严重影响程序稳定性和性能。为解决这些问题，垃圾回收机制应运而生，目的是自动检测并回收不再被程序使用的内存。

#### 1\. 标记-清除算法（Mark-Sweep）

JVM堆内存用于存储Java对象，对于堆内存如何管理就需要有些考究了，首先有一块空白内存等待被使用。当新对象需要被分配空间的话，我们可以采用简单直接的遍历方式按需分配即可，等到分配的对象不需要时，则需要清理对应空间，后面再创建新对象时可以使用已回收空间，这就是我们直接想到的标记-清除算法：

![](https://staticcdn1-5.umiwi.com/epms_ebook/1c6d91fe9fbf6b0fe7ef97a55a43304a.jpg?x-oss-process=image/resize,w_2560,m_lfit align="left")

这种方法简单直接，但它的问题是内存碎片化，零碎的内存多了，当一个新的大的对象要创建时，会遇到连续的空间不够的情况，这就需要对已经存活的对象进行迁移地址，这影响到了内存分配效率。

#### 2\. 复制算法（Copying）

为解决内存碎片问题，复制算法被引入。它将内存分为两个相等的区域，每次只使用其中一个。垃圾回收时，算法会将活动对象从当前区域复制到另一个区域，然后清除当前区域的所有对象，从而避免碎片化。

![](https://staticcdn1-5.umiwi.com/epms_ebook/0ae1829f51d3300b1698c732818fba5b.jpg?x-oss-process=image/resize,w_2560,m_lfit align="left")

赋值算法依然有其局限性：

1. **内存利用率**：复制算法需要将内存分成两个区域，一次只使用其中一个区域来存放对象。这意味着在任何时刻，有一半的内存空间不被利用，从而降低了内存的有效利用率。
    
2. **对象复制开销**：虽然复制存活对象到另一区域可以避免碎片化，但这个过程需要复制对象，并更新引用这些对象的所有指针。对于存活对象较多的场景，这种复制动作可能会带来不小的性能开销。
    

这就导致在遇到**大对象**（涉及到对象不断复制复制）**、对象多**的情况下，也会有性能问题。

#### 3\. 分代收集算法

分代收集算法基于这样一个观察：不同对象的生命周期不同。因此，JVM的内存被划分为年轻代（Young Generation）和老年代（Old Generation），以不同的方式管理不同寿命的对象。

* **年轻代**：新创建的对象首先被分配到年轻代。年轻代进一步细分为Eden区、From Survivor区和To Survivor区。大部分对象在这里被快速回收。
    
* **老年代**：经过多次垃圾回收依然存活的对象，会被移动到老年代。老年代的对象生命周期较长，垃圾回收频率较低。
    

#### 4\. Eden区、From、To区

* **Eden区**：新生成的对象首先被分配到这里。
    
* **From和To Survivor区**：用于实现复制算法的一部分。在每次年轻代垃圾回收时，存活的对象会从Eden区和一个Survivor区（例如From）复制到另一个Survivor区（例如To），然后清空Eden区和之前的From Survivor区。
    

#### 5\. 标记-整理算法（Mark-Compact）

用于老年代的标记-整理算法，它在标记阶段后不是简单地清除未标记对象，而是将所有存活的对象移动到内存的一端，然后清理边界以外的内存，从而解决内存碎片问题，适用于老年代中对象生命周期较长的特点。

### **结论**

JVM的垃圾回收机制通过引入复制算法、分代收集算法、Eden区、From/To区的概念，以及标记-整理算法等技术，有效地解决了内存管理中的关键问题，如自动内存回收、内存碎片化，以及不同生命周期对象的高效管理。这些技术的引入，是基于对程序内存使用模式的深入理解和长期观察的结果，目的是优化内存回收过程，提高程序性能和稳定性。
