Loading

Java垃圾收集器总结

一道面试题,单拿出来放一篇文章中方便找

203. 说一下 jvm 有哪些垃圾回收器?

Serial系列

Serial和SerialOld分别是这个系列面向新生代和老年代的垃圾收集器,它们的特点就是单线程,而且,一旦开始工作,整个工作过程必须中断用户线程的执行以保证垃圾回收的正确性。

听起来很无法接受,但由于其简单性,它无需维护复杂的数据结构所以内存占用很小,它的单线程工作模式让它无需处理多个垃圾回收线程之间的并发安全,所以,在嵌入式系统、微服务组件、移动智能设备上,它还是有用武之地的。

ParNew

Serial的多线程版本,注意,不是SerialOld的,所以是个新生代垃圾回收器。

除了使用多个线程对新生代进行清理,和Serial并无什么本质的差别,工作过程中也必须中断用户线程。

只有ParNew才能和后面出现的老年代垃圾收集器CMS合作使用

Parallel系列

Parallel系列有两个收集器,Parallel Scavenge和Parallel Old,是多线程的新生代和老年代收集器。

Parallel Scavenge和ParNew相比,亮点是可控的停顿时间和吞吐量

  1. -XX:+MaxGCPauseMillis:指定垃圾回收器的最大停顿时间
  2. -XX:+GCTimeRatio:指定垃圾收集器占用总运行时间的比值

垃圾收集器显然不会魔法,所以,它要尽量满足上面两个参数设定的值就必须做一些牺牲,比如,如果-XX:+UseAdaptiveSizePolicy参数开着的情况下,它会动态调整新生代的大小,新生代更小了回收起来就更快了,但随之而来的问题就是更多的对象进入老年代,以及新生代的回收越来越频繁,这时吞吐量可能不增反降。

Parallel Old用于和Parallel Scavenge配合,完成新生代以及老年代的共同回收,否则,它就只能用Serial Old了。

CMS

并发的标记清理垃圾回收器,既然是垃圾清理,一定是个老年代回收器喽。

如何区分垃圾回收的并发和并行

前面我们已经看到了很多Par开头的垃圾回收器,ParNew和Parallel系列,它们都是Parallel的意思,这个单词的意思是并行,它主要体现了这些垃圾回收器在垃圾回收阶段,多个垃圾回收线程可以利用多个CPU进行并行工作。

而CMS开头的单词是Concurrency,也就是并发的意思,它的主要含义在于,这个垃圾回收器进行垃圾回收的部分阶段可以和用户线程并行,它们共同交替使用CPU,而不用像之前的垃圾回收器一样完全停止用户线程,虽然在某些阶段仍需完全停止用户线程,但这些阶段占用的时间已经很少了。

现代垃圾回收器一般具有与用户线程并发的能力,比如CMS,而它的名字里没有提到Parallel,并不代表它垃圾回收时只有单个线程,只是说选择了把与用户线程并发这个亮点放在了名字里。

CMS的主要亮点就是,它的垃圾回收阶段可以有相当一部分与用户线程并发。

一般来说,这种垃圾收集器的工作阶段有四个:

  1. 初始标记:需要停止用户线程的阶段,只是简单的找下GC Roots,对它们进行一下标记,占用时间很短
  2. 并发标记:无需停止用户线程的阶段,并发的对它处理范围内所有对象进行扫描、标记,占用时间较长
  3. 重新标记:需要停止用户线程的阶段,占用时间很短。由于与用户线程并发执行,所以并发标记过程中,用户线程有可能改变引用关系,所以需要采用一些机制简单的进行处理,一般有两种算法:
    1. 增量更新(CMS采用的方式)
    2. 原始快照
  4. 并发清除:无需停止用户线程的阶段,将未被标记的对象清除

所以,垃圾回收中两个占用时间比较长的工作都与用户线程并发了,这并不会减少垃圾清理占用的时间,也就是说不会提高系统的吞吐量,但是可以让用户获得更短的停顿时间,然后系统性能在垃圾回收时平缓下降。

G1收集器

G1回收器也是一个可以和用户线程并发的垃圾回收器,所以它的工作阶段和CMS差不多,只不过它采用了原始快照算法来重新标记,而且最后一个清除阶段必须停止用户线程。

G1回收器面对整堆进行清理,也就是Mixed GC,它打破了新生代老年代的限制,采用将内存划分为一些大小相等的Region,对每个Region都通过系统的运行时信息统计它的回收收益比,然后优先选择回收收益大的Region进行清除。

G1也有的Region也分类型,比如新生代、Eden、Survivor、老年代、大对象。

G1收集器的Region设计让它可以一次不清除整个新生代或老年代,一次只清理几个Region,小步快跑。所以它很自然的支持-XX:+MaxGCPauseMillis,即设置最大停顿时间。用户需要知道,如果它设置的太小,很容易让垃圾收集的速度赶不上产生垃圾的速度。

同时,也是由于分Region设计,这代表它要对每一个Region维护一个记忆集来存储跨Region引用,这大大的增加了内存消耗。不过现在的系统都是为了提高速度,多用一些内存也没什么。

其它收集器

ZGC

posted @ 2022-07-05 10:37  yudoge  阅读(78)  评论(0编辑  收藏  举报