天天看点

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

Java 常见的垃圾回收器

垃圾回收器 (GC, Garbage Collector)是和具体的 JVM 实现紧密相关。

Java 虚拟机针对新生代和年老代分别提供了多种不同的垃圾收集器。

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

垃圾收集器

Serial GC

Serial GC ,是新生代的垃圾回收器, Serial 体现在其收集工作是单线程的,并且在垃圾收集过程中,其他线程阻塞,进入 Stop Thre World 状态。新生代使用的 Serial 垃圾回收器,是基于复制算法的。

-XX:+UseSerialGC
           

复制

Paralel Scavenge

Parallel Scavenge 收集器,是一个新生代的垃圾回收器,采用的是复制算法。关注的是程序到达一个可控制的吞吐量(Thoughput ,CPU 用于运行用户代码的时间/CPU总消耗时间)。吞吐量= 运行用户代码时间/(运行用户代码时间+垃圾收集时间). 高吞吐量可以最高效率的利用 CPU 时间。尽快完成程序的运算任务。值得关注的是 Parallel Scavenge 收集器有个自适应调节参数。

这个参数就是:-XX:UseAdaptiveSizePolic。这是一个开关参数,当这个开关打开之后,就不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比列(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PertenureSizeThreshold)等参数细节了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。

-XX:+UseParallelGC
           

复制

可以直接设置暂停时间或者吞吐量等目标, JVM 会自动进行适应性调整。

-XX:MaxGCPauseMillis=value
-XX:GCTimeRatio=N // GC时间和用户时间比例 = 1 / (N+1)
           

复制

ParNew

ParNew 垃圾回收器是 Serial 收集器的多线程版本。采用的也是复制算法。包括 Serial 收集器可用的所有控制参数。也就是说可并行的进行垃圾回收。

可通过-XX:ParallelGCThreads 参数来限制垃圾收集器的线程数。

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

ParNew

-XX:+UseParNewGC
           

复制

Serial Old

Serial Old 是 Serial 垃圾收集器的老年代版本,同样是个单线程的收集器,是基于标记-整理算法。

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

Serial Old

Parallel Old

Parallel Old 是 Parallel Scavenge 的老年代版本,使用的是多线程-标记整理算法。JDK1.6 才开始使用。ParallelScavenge 是可以保证新生代的吞吐量优先,但是不能保证整体吞吐量。Parallel Old 是为了在老年代同样提供吞吐量优先的垃圾回收器。

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

ParallelOld

CMS

CMS 是基于标记清除算法,设计的目的是减少停顿时间。基于标记清除算法,会存在内存碎片化的问题。

-XX:+UseConcMarkSweepGC
           

复制

CMS 处理流程

  1. 初始标记(CMS-initial-mark) ,会导致stw;
  2. 并发标记(CMS-concurrent-mark),与用户线程同时运行;
  3. 重新标记(CMS-remark) ,会导致swt;
  4. 并发清除(CMS-concurrent-sweep),与用户线程同时运行;
  5. 并发重置状态等待下次CMS的触发(CMS-concurrent-reset),与用户线程同时运行;
Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

CMS处理流程

G1

G1 本质上是一个分带垃圾回收器。 Garbage First 垃圾回收器相对 CMS 垃圾回收器,有两个改进:

  1. 基于标记-整理 算法,不产生内存碎片。
  2. 可以准确的控制停顿时间,在不牺牲吞吐的情况下实现低停顿的垃圾回收。

G1 为了避免全区域垃圾收集,把堆内存划分为大小固定的几个独立区域,并跟踪这些区域的回收进度。同时在后台维护一个优先列表,每次根据收集时间的,优先回收垃圾最多的区域。

G1 引入了额外的概念,Region。G1垃圾回收器把堆划分成一个个大小相同的Region。在HotSpot的实现中,整个堆被划分成2048左右个Region。每个Region的大小在1-32MB之间,具体多大取决于堆的大小。

G1垃圾回收器的分代也是建立在这些Region的基础上的。对于Region来说,它会有一个分代的类型,并且是唯一一个。即,每一个Region,它要么是young的,要么是old的。还有一类十分特殊的Humongous。所谓的Humongous,就是一个对象的大小超过了某一个阈值——HotSpot中是Region的1/2,那么它会被标记为Humongous。如果我们审视HotSpot的其余的垃圾回收器,可以发现这种对象以前被称为大对象,会被直接分配老年代。而在G1回收器中,则是做了特殊的处理。

G1并不要求相同类型的region要相邻。换言之,就是G1回收器不要求它们连续。当然在逻辑上,分代依旧是连续的。因此,一种典型的分配可能是:

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

image

其中E代表的是Eden,S代表的是Survivor,H代表的是Humongous,剩余的深蓝色代表的是Old(或者Tenured),灰色的代表的是空闲的region。每一个分配的Region,都可以分成两个部分,已分配的和未被分配的。它们之间的界限被称为top。总体上来说,把一个对象分配到Region内,只需要简单增加top的值。这个做法实际上就是bump-the-pointer。过程如下:

Java 常见的垃圾回收器Java 常见的垃圾回收器Serial Old

image

Region可以说是G1回收器一次回收的最小单元。即每一次回收都是回收N个Region。这个N是多少,主要受到G1回收的效率和用户设置的软实时目标有关。每一次的回收,G1会选择可能回收最多垃圾的Region进行回收。与此同时,G1回收器会维护一个空间Region的链表。每次回收之后的Region都会被加入到这个链表中。每一次都只有一个Region处于被分配的状态中,被称为current region。在多线程的情况下,这会带来并发的问题。G1回收器采用和CMS一样的TLABs的手段。即为每一个线程分配一个Buffer,线程分配内存就在这个Buffer内分配。但是当线程耗尽了自己的Buffer之后,需要申请新的Buffer。这个时候依然会带来并发的问题。G1回收器采用的是CAS(Compate And Swap)操作。