【Java虚拟机】JVM学习笔记之GC

时间:2022-02-24 09:58:32

JVM学习笔记二之GC

GC即垃圾回收,在C++中垃圾回收由程序员自己来做,例如可以用free和delete来回收对象。而在Java中,JVM替程序员来执行垃圾回收的工作,下面看看GC的详细原理和执行过程。

1.对象已死?

1.1 引用计数法

Java GC不采用引用计数法是因为无法解决对象互相引用导致无法回收的问题。

1.2 可达性分析法

从GC ROOT开始搜索,搜索不到的并且经过第一次标记、清理后未复活的对象。

GC ROOT包括:

  • 虚拟机栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • Native方法引用的对象

1.3 四种引用

强引用

Object object = new Object();

只要强引用存在,就不会被回收。

软引用

描述一些还有用但是非必需的对象,当系统将要发生OOM之前,会对软引用的对象进行二次回收。

可以用SoftReference来实现软引用。

弱引用

也是用来描述一些非必需的对象,只能生存到下次垃圾回收之前,可以用WeakReference来实现。

虚引用

引用强度最弱的一种引用,可以用PhantomReference来实现虚引用。

2.垃圾回收算法

2.1 标记-清除算法

【Java虚拟机】JVM学习笔记之GC

两个不足:

  • 标记和清除两个阶段效率都很低
  • 会造成大量的内存碎片,而为大对象分配内存需要大段的连续内存

2.2 复制算法

【Java虚拟机】JVM学习笔记之GC

将内存分为两块同等大小的区域,一块用完时把上面存活的对象复制到另一块内存上,然后对这块内存进行集体回收。

这种做法的坏处是空间利用率太低。

2.3 标记-整理算法

【Java虚拟机】JVM学习笔记之GC

标记整理算法是让所有存活对象都向一端移动,然后直接清理掉端外的所有内存。

2.4 分代收集算法

在现在的主流JVM中,采用的都是分代回收算法,对于经常需要回收的新生代,采用的是复制算法,而对于存活率高的老年代,采用的是标记清除和标记整理算法。

3.垃圾收集器

【Java虚拟机】JVM学习笔记之GC

3.1 Serial收集器

单线程收集器,在进行垃圾收集时会停止所有其他线程,即stop-the-world。

【Java虚拟机】JVM学习笔记之GC

-XX:+UseSerialGC

3.2 ParNew收集器

ParNew是Serial的多线程版本,但是还是会有stop-the-world的问题。

【Java虚拟机】JVM学习笔记之GC

-XX:+UseParNewGC

3.3 Parallel Scavenge收集器

类似ParNew收集器,但是更关注系统的吞吐量,如果更注重吞吐量应该使用Parallel Scavenge收集器。

3.4 Serial Old收集器

Serial收集器的老年代版本,主要进行老年代的收集。

3.5 Parallel Old收集器

SerialOld的多线程版本。

3.6 CMS(Concurrent Mark Sweep)收集器

【Java虚拟机】JVM学习笔记之GC

-XX:+UseConcMarkSweepGC

收集器是一种以获取最短回收停顿时间为目标的收集器。

但是有以下缺点:

  • 由于采用多线程占用了CPU资源,可能导致系统性能下降
  • 无法处理浮动垃圾:由于GC线程与用户线程同时运行,导致GC过程中还可能有垃圾产生
  • 由于CMS是标记-清除算法,所以会造成内存碎片的产生

3.7 G1收集器

【Java虚拟机】JVM学习笔记之GC

有如下几点特点:

  • 并发与并行:通过利用多CPU、多核来减少stop-the-world的时间
  • 分代回收:可以独当一面,不需要其他收集器的配合
  • 空间整合:从整体上看是标记-整理算法,不同于CMS的标记-清除算法,可以有效地减少内存碎片的产生
  • 可预测的停顿:采用Region进行分区,可以把一个Region分配到任意一个区域,然后会选择清理收益最高的一个Region(相比于直接清理年轻代或老年代来说清理的粒度更细,从而就导致效率提升)。

垃圾回收过程

【Java虚拟机】JVM学习笔记之GC

【Java虚拟机】JVM学习笔记之GC

Eden:Survivor From:Survivor TO = 8 : 1 : 1

为什么Survivor区域要分为两个:让对象在之间来回复制,从而计算对象存活的年龄。
http://ifeve.com/jvm-yong-generation/

什么时候会触发GC

由于GC是分代回收,所以这个问题分为什么时候触发Minor GC(年轻代)、Major GC(老年代)和Full GC(年轻代+老年代):

  • Minor GC:当Eden区域满了的时候,用户又需要创建对象,需要在堆中的年轻代分配内存,此时就会触发Minor GC。Minor GC主要采用复制收集算法,由于年轻代中的对象都很小,所以GC时间很短,经过Minor GC后没有被回收的对象会被移动到Survivor区域,并且年龄加一,当年龄超过阈值时会被移动到老年代中。
  • Major GC:当对象从年轻代移动到老年代之前,会检测老年代中剩余的空间是否足够装得下这些对象,所以当老年代内存不足时会触发Major GC。Major GC主要采用的标记-整理算法(CMS)。

  • Full GC:除了直接调用System.gc()之外,还有以下四种情况:
    • 老年代内存不足
    • 永生代内存不足
    • CMS GC时出现promotion failed和concurrent mode failure
    • 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间