JVM--垃圾回收与算法

113 阅读5分钟

一、如何确定垃圾

1、引用计数法

在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单 的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关 联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么这个对象就是可回收 对象。

2、可达性分析

为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。通过一系列的“GC roots” 对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记 过程。两次标记后仍然是可回收对象,则将面临回收。

二、垃圾回收算法

1、标记清除算法(Mark-Sweep)

最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清 除阶段回收被标记的对象所占用的空间。如图:

image.png

优点: 速度快

缺点: 容易产生内存碎片。如当创建一个新对象时,该对象所需空间比列表中的空闲内存大,则无法使用其地址,就产生了内存碎片。

2、复制算法(copying)

为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小 的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用 的内存清掉,如图:

image.png

image.png

image.png

优点: 不会产生内存碎片

缺点: 可用内存被压缩到了原来的一半,存活的对象太多的话效率会大大降低。

3、标记整理算法(Mark-Compact)

结合了以上两个算法,为了避免缺陷而提出。标记阶段和 Mark-Sweep 算法相同,标记后不是清 理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象。如图:

image.png

优点: 减少了内存碎片的产生。

缺点: 移动需要消耗时间,速度减慢。

三、分代回收机制

分代收集法

分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不同生命周期将内存 划分为不同的域,一般情况下将 GC 堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃 圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法

1)新生代与老年代:新生代的垃圾回收频率较高,并且速度较快,一般用于存放临时的对象。老年代则相反,并且一般用于存放长期使用的对象。

2)伊甸园是对象创建时初始的存放位置。当内存不够时会发生一次垃圾回收(Minor GC),并将标记好的内存地址放入幸存区To中然后执行复制算法的垃圾回收,并使其寿命+1(默认都是0)

3)再次进行垃圾回收时会把幸存区中的对象也进行一次复制算法的垃圾回收(Minor GC),并使留下的对象寿命+1

4)当某个对象的寿命超过了一个阈值,会使其从幸存区中移动到老年代中。

5)当老年代的空间不足,新生代的空间也不足时,会发生一次Full GC

6)Minor GC和Full GC会引发stop the world,使其他用户线程暂停工作,直到垃圾回收结束。假设线程不停止工作,在垃圾回收的时候会设计到对象的复制移动和删除,会导致线程运行时混乱。

新生代与复制算法

目前大部分 JVM 的 GC 对于新生代都采取 Copying 算法,因为新生代中每次垃圾回收都要 回收大部分对象,即要复制的操作比较少,但通常并不是按照 1:1 来划分新生代。一般将新生代 划分为一块较大的 Eden 空间和两个较小的 Survivor 空间(From Space, To Space),每次使用 Eden 空间和其中的一块 Survivor 空间,当进行回收时,将该两块空间中还存活的对象复制到另 一块 Survivor 空间中。

老年代与标记复制算法

而老年代因为每次只回收少量对象,因而采用 Mark-Compact 算法。

  1. JAVA 虚拟机提到过的处于方法区的永生代(Permanet Generation),它用来存储 class 类, 常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。
  2. 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目 前存放对象的那一块),少数情况会直接分配到老生代。
  3. 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From Space 进行清理。
  4. 如果 To Space 无法足够存储某个对象,则将这个对象存储到老生代。
  5. 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。
  6. 当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被 移到老生代中。

image.png