JAVA基础

jvm 垃圾回收机制

2017-10-20  本文已影响23人  aaron1993

1. 前言

网上关于jvm gc的文章有很多,写这篇文章不是有什么新东西要讲,主要原因是工作时也偶尔碰到比如full gc,jvm参数设置或者使用jdk自带的一些命令查看gc或者内存占用等,每次碰到这种情况都要百度一番,看一些文章,但是不记下来久而久之就遗忘了,等到下次出现时又需要查看一番。本文主要做一些笔记,下次就不用去百度了。

注:本文内容主要来自于博客(在最后参考部分已经列出)以及「深入理解java虚拟机」一书。

2. GC算法和垃圾回收器

2.1 GC类型

  1. partial gc
    即只对内存的一部分进行GC,主要包括
    • Young GC
    • Old GC CMS垃圾收集器专有,其他的old区收集器进行gc等同于fullgc,会同时收集young,old和permenant
    • MixGC G1专有,收集部分young区和部分old区
  2. full gc
    收集young,old和permenant区

2.2 GC算法

2.2.1 标记清除

标记清除算法分为"标记"和“清除”两个阶段,“标记”阶段标记出可以回收的对象,“清除”阶段回收被标记的内存。过程如下图:

标记清除_两个阶段
存在的问题
注:图片来自于图片出处
  1. 清除的内存比较分散,回收起来比较耗时
  2. 清除之后存在大量的内存碎片。

2.2.2 复制算法

hotspot新生代垃圾收集器基本采用这种算法,理论上它将内存分为大小相同的两块(实际上实现时不是按这种比例划分),分配内存时始终只使用其中一块,内存不够用时进行GC,将当前使用的内存中存活的对象复制到另一块空闲内存上,然后集中清空当前使用内存即可。如下图所示:

复制算法.jpg
图片出处
优点
由于每次回收都会将存活对象复制到另一块区域的连续空间上,一方面不会有内存碎片存在,没有内存碎片的存在,分配新的内存时只需要在在连续的未使用区域移动堆顶指针,按序分配内存即可。

实际上垃圾回收器使用这种算法回收年轻代时,并不是按照1:1划分内存的,而是将内存划分为一块Eden区和两块Survivor, Hotspot默认Eden:Suvivor-1:Survivor-2=8:1:1,分配内存时只使用Eden区和其中一块Survivor,GC时对Eden区和这块正在使用的Survivor进行回收,然后将存活的对像复制到另一块未使用的Survivor上。

2.2.3 标记-整理算法

标记整理算是Hotspot回收老年代的算法,如下图所示:

标记-整理.jpg
图片出处
如上图,标记整理算法不将内存分为两块,它使用全部的内存,在垃圾回收时它将存活的对象移动的内存的一端,然后清理掉边界以外未使用的内存。

2.2.4 分代收集

分代收集算法不是一种新的算法,而是使用上面算法的组合去管理内存的不同区块,分代算法将内存分为年轻代和老年代,各自使用不同的收集算法。
Hotspot中将内存分为年轻代(Young)和老年代(Tenure),年轻代又被划分为Eden区和2个Survivor区。年轻代采用复制算法,老年代采用标记-整理算法。如下图所示:

jvm内存分代.png

2.2 垃圾回收器

下图是Hotspot虚拟机包含的垃圾回收器:

垃圾回收器.jpg
图片出处
实线分割线上面是年轻代垃圾回收器,下面是老年代垃圾回收器。G1没有传统的内存分代。

虚线连接线表示他们可以搭配共同工作完成对年轻代和老年代的垃圾回收。

2.2.1新生代垃圾回收器

并不打算写,参考垃圾回收器
1. Serial
使用复制算法,这种垃圾回收器会阻塞其他所有工作线程

2. ParNew
Serial的并行版本,它进行垃圾回收时也会暂停其他的工作线程,和Serial的区别就是它使用多个线程并行回收。

3. Parallal Scavenge
这是一个吞吐量优先的新生代回收器,所谓吞吐量:
(CPU运行用户程序时间) / (运行用户代码时间 + 垃圾回收时间)
吞吐量优先说明它适合计算型任务(比如Spark的executor使用这种回收器),相应时间有限适合交互的程序,很多网站的web后台使用ParNew。

2.2.2老年代垃圾回收器

1. Serial Old
Serial的老年代版本,使用标记-整理算法,单线程收集,Stop-the-world。
在CMS垃圾回收器出现Concurrent Mode Failure后作为CMS的备选。
2. ParallelOld
Parallel Scavenge的老年代版本,标记-整理算法,使用多线程回收,Stop-the-world,

2. CMS
使用标记-清除算法(这种算法会产生内存碎片),回收过程尽力过个步骤如下:

CMS垃圾回收过程.png
图片出处
  1. 初始标记和重新标记阶段,都是Stop-the-world,但是时间很短,不像其他两个整个过程都是Stop-the-world

  2. 由于并发清除和用户线程同时运行,也就是说清理阶段可能会有新的内存分配和新的垃圾产生,这种垃圾称为“浮动垃圾”,显然这中垃圾只能等到下一次GC了。并发清除过程中新的内存分配产生也就意味着CMS不能像其他垃圾回收器一样:在老年大沾满了之后再回收,CMS必须在老年代有一定的预留空间是回收,默认是老年代达到68%时开始回收。如果预留空间不够会触发"Concurrent Mode Failture",此时CMS退化为Serial Old垃圾回收器。

  3. 由于CMS采用标记-清除,所以会产生内存碎片

2.3 G1垃圾回收器

G1是Garbage First的简称,G1垃圾回收器对内存的划分和上面几种都不一样,如下图所示:

g1内存分代.png

如上图,G1将内存分为一个个的Region,尽管依然存在eden,survivor,old区,但是它们不是连续的分布,其中Humongous用来分配给特别大的对象(大雨region的一半)。

G1在GC时采用一种启发式的算法,他根据指定的jvm参数MaxGCPauseMillis(最大垃圾回收停顿时间)来决定本次收集多少内存(这个和ParallelOld还是挺像的)

关于G1的原理,以及G1的日志参数可以参考:
1 . 深入理解 Java G1 垃圾收集器
2 . G1 垃圾收集器入门
3 . 理解G1垃圾收集器日志

G1相关参数:

注:由于G1的gc(YoungGC和MixGC)会扫描整个young区,而GC控制垃圾收集时间的方式是通过通知yong区的region个数来达到的,所以不要使用-Xmn或者-XX:NewSize或-XX:NewRatio去指定young区大小,不然就扫描整个young内存了,耗时

2.4 jvm参数

1. heap大小相关

2.4.1 GC日志相关参数

3. JDK常用命令

3.1 jps

列出当前所有运行jvm进程

3.2 jstat

jstat用来查看GC和堆相关信息, 命令格式:
jstat <option> vmid [interval [count]] 其中[]表示可选,interval表示采样间隔时间(s|ms),count表示输出结果数,比如:
jstat -gc 2141 3s 3 表示输出2141号jvm进程gc统计信息(-gc选项) , 相隔3s统计一次,输出3行
常用命令

3.3 jmap

jmap用于生成当前内存转储快照,一般是在怀疑有内存泄漏时输出内存快找供后面分析。
常用命令

3.4 jstack

用于输出jvm进程中所有线程执行的方法的堆栈,在发现java程序没有响应时可是使用jstack查看java程序所有的线程运行状况,是否阻塞。

3.5 jinfo

查看jvm参数信息,格式为:
jinfo <option> vmid
如:

本文参考

  1. JVM初探- 内存分配、GC原理与垃圾收集器
  2. 深入理解 Java G1 垃圾收集器
  3. Java HotSpot VM options
  4. JVM系列三:JVM参数设置、分析
  5. G1 垃圾收集器入门
  6. 理解G1垃圾收集器日志
  7. GC种类 R大的回答
上一篇下一篇

猜你喜欢

热点阅读