《深入理解Java虚拟机》(二)--垃圾收集器与内存分配策略(3

2018-05-28  本文已影响80人  蓝色_fea0

垃圾收集器

如果垃圾收集算法是内存回收的方法论,垃圾回收器就是内存回收的具体实现。JVM规范中没有对垃圾回收器的任何规定。这本书中讲了基于JDK1.7 update14之后的HotSpot虚拟机,这个虚拟机所包含的所有收集器如下图。


HotSpot虚拟机的垃圾收集器

图中展示了7种作用于不同分代的收集器,连线表示两个收集器之间可以搭配使用。收集器所在的区域表示的是它所能作用的区域。

1/1 Serial 收集器:

Serial收集器是最一个单线程收集器,它是作用于新生代的,就是它在进行垃圾回收运作的期间,必须去暂停其他所有的工作线程。这就是“Stop The World”的由来(后面成为STW),全世界都停了,就你自己在这收集垃圾。
下图描述了Serial收集垃圾的过程:


Serial收集器收集垃圾的过程

优点:

简单、效率高(和其他单线程的收集器相比的话)。

缺点:

会造成STW。

应用场景:

对于限定单个CPU的环境来说,Serial收集器由于没有现成交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

1/2 ParNew 收集器:

ParNew 收集器其实就是Serial 收集器的多线程版,除了使用多线程进行垃圾回收之外,还包括了Serial收集器的所有控制参数、收集算法、STW、对象分配规则、等等。 ParNew 收集器的工作过程如下:


ParNew 收集器的工作

ParNew 收集器除了是多线程之外,其他与Serial收集器相比并没有太多创新之处,但是它确是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为除了Serial收集器,只有它能与CMS收集器(后面会说道)配合工作,ParNew 收集器在单CPU的环境中没有Serial收集器的效果好,但是随着CPU的增加,它的效果会越来越好(现在CPU越来越多了,比如我的电脑就是8个,如果是服务器的话就更多了)。它默认开启的线程数是CPU的数量相同。

并发和并行的概念

这两个名词老是然在一起,我来说说自己的理解吧。

并行:

并行就是在时间上,也就是我们生活中所说的真正意义上的一起运行。

并发:

并发包括并行(就是继承于并行),但是还包括 程序通过CPU的时间片段,实现一个看起来是一起运行的情况。简单说就是并发是并行的子类,它还新增加了一个看起来是一起运行的方法。

1/3 Parallel Scavenge 收集器

Parallel Scavenge 是一个新生代收集器,它是使用复制算法的收集器,英文的意思是并行收集器。它的关注点是达到一个可控制的吞吐量。吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如虚拟机总共运行100分钟,其中垃圾回收花掉1分钟,那吞吐量就是99%。程序的停顿时间越短,那么用户的体验肯定越好(因为不卡顿),但是程序总体的运行时间不一定快,举个栗子,一个程序每隔10秒停顿100毫秒,另一个程序每隔100秒停顿500毫秒。那么用户体验上肯定是第一个程序好(因为用户感觉不到100毫秒的停顿),但是第二个程序运行的更快,也就是吞吐量高。

应用场景:

高吞吐量可以高效率利用CPU时间,尽快完成运算任务,主要适合在后台运算而不需要太多交互的任务。除此之外,Parallel Scavenge 收集器具有自适应调节策略,它可以将内存管理的调优任务交给虚拟机去完成。自适应调节策略也是Parallel Scavenge与 ParNew 收集器的一个重要区别。

Serial Old 收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理的算法。

应用场景:

Serial Old 收集器主要有两个用途:一个是在JDK1.5之前与Parallel Scavenge 收集器搭配使用(因为Parallel Scavenge 收集器是新生代收集器,需要一个老年代收集器的配合)。另一个是作为CMS 收集器的后备预案,在它发生 Concurrent Mode Failure的时候使用。

Parallel Old 收集器

Parallel Old收集器是Parallel Scavenge 收集器的老年代版本,使用多线程和标记-整理算法,这个收集器是JDK1.6之后才开始提供,从HotSpot虚拟机的垃圾收集器的图中也可以看出,Parallel Scavenge 收集器无法与CMS收集器配合工作(因为一个是为了吞吐量,一个是为了客户体验(也就是暂停时间的缩短))。

应用场景:

与Parallel Scavenge 收集器配合使用,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge 收集器加Parallel Old 收集器。

CMS 收集器

CMS(Concurrent Mark Sweep(意思是并发 标记 清除))收集器是一种以获取最短停顿时间(用户体验好)为目标的老年代收集器。从名字上可以看出,它是使用标记-清除算法来实现的一个并发收集器,它的运行过程分为4个步骤:

步骤:

由于整个过程中,耗时最长的并发标记和并发清理都是与用户线程一起执行的,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。下面是CMS收集器运行示意图:


image.png

缺点:

CMS虽然是一个并发低停顿的收集器,但是它仍然有以下几个明显的缺点:

G1 收集器

G1(Garbage-First)收集器是当今收集器技术发展的最前沿的成果之一,不过这本书是JDK1.7的时候,G1收集器并没有被普遍使用,现在已经JKD11了....在JDK9的时候就已经将G1收集器设为默认的收集器了,而且这本书中对这个收集器的描述也不像其他的收集器那样清晰(简直就是天书),所以以下内容可能有我理解不好的地方,欢迎指出。

特点:

G1收集器是面向服务端的收集器,它的思想就是首先回收尽可能多的垃圾(这也是Garbage-First名字的由来),G1的所有过程都是STW的。

Region(G1的分区)

虽然G1有分代的概念,但是实际上G1是将堆划分成若干个大小相同的独立分区(默认是2000个),然后将这些分区分到不同的角色上(新生代、老年代),但是哪个代分配多少个是不确定的。

Card(卡片)

在每个分区内部又被分成了若干个大小为512 Byte卡片(Card),标识堆内存最小可用粒度所有分区的卡片将会记录在全局卡片表(Global Card Table)中,分配的对象会占用物理上连续的若干个卡片,当查找对分区内对象的引用时便可通过记录卡片来查找该引用对象(见RSet)。每次对内存的回收,都是对指定分区的卡片进行处理。

Remembered Set (已记忆 集合)

在G1收集器中,Region之间的对象引用(以及其他的收集器中新生代和老年代之间的对象引用),虚拟机都是使用RRemembered Set来避免全堆的扫描(生成可达链)。在G1中每个Region都有一个对应的Remembered Set,虚拟机发现程序在堆Reference型的数据进行写操作时,会产生一个Write Barrier(写屏障)暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中,如果是,便通过CardTable(卡片记录表)把相关引用的信息记录到被引用的对象所属的分区的Remembered Set中,当进行垃圾回收时,在GC跟节点的枚举范围中加入Remembered Set即可保证不对全堆扫描。

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下几个步骤(与CMS很类似):

优点分析(参考自详解 JVM Garbage First(G1) 垃圾收集器):

G1是一款非常优秀的垃圾收集器,不仅适合堆内存大的应用,同时也简化了调优的工作。通过主要的参数初始化最大堆空间、以及最大容忍的GC暂停目标,就能得到不错的性能;同时,我们也看到G1对内存空间的浪费较高,但通过首先收集尽可能多的垃圾(Garbage First)的设计原则,可以及时发现过期对象,从而让内存占用处于合理的水平。

GC日志

2016-03-20T14:34:55.118-0800: [GC [PSYoungGen: 6123K->400K(38912K)] 6123K->400K(125952K), 0.0012070 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2016-03-20T14:34:55.119-0800: [Full GC [PSYoungGen: 400K->0K(38912K)] [ParOldGen: 0K->282K(87040K)] 400K->282K(125952K) [PSPermGen: 2622K->2621K(21504K)], 0.0084640 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

最前的数字”2016-03-20T14:34:55.118-0800”和“2016-03-20T14:34:55.119-0800”表示GC发生的时间。
日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,如果有Full表明这次GC发生了STW。
下面的“[PSYoungGen”、“[ParOldGen”表示使用的垃圾收集器的名字
[PSYoungGen表示Parallel Scavenge 收集器,[ParOldGen表示是Parallel OId收集器。
后面括号内的“6123K->400K(125952K)”表示的是:
GC前该内存区域已使用的容量->GC后该内存区域已使用的容量(该内存区域的总容量)。

垃圾收集器参数总结

image.png

G1收集器参数:


image.png
上一篇 下一篇

猜你喜欢

热点阅读