垃圾回收器
GC算法有四种:复制算法、标记清除、标记整理、分代收集
垃圾回收器是这四种算法的实现
预先了解
垃圾回收器种类
-
Serial:串行,为单线程设计,且只设有一个垃圾回收线程进行垃圾回收,会暂停所有用户线程。
-
Parallel:并行,多个垃圾回收线程进行垃圾回收,暂停所有用户线程。适用于弱交互。
-
CMS:并发,用户线程和垃圾回收线程同时执行,不需要停顿用户线程。适用于强交互。
-
G1:Garbage First,将堆内存分割成不同的区域然后并发的对其进行垃圾回收
参数说明
- DefNew:Default New Generation
- Tenured:Old
- ParNew:Parallel New Generation
- PSYoungGen:Parallel Scavenge
- ParOldGen:Parallel Old Generation
Server/Client模式说明
-
一般都是用到的server模式,client模式基本不会用到
-
操作系统:
-
32位Windows系统,不论硬件如何都默认使用Client模式
-
32位其他操作系统,2G内存并且有2个CPU之上的配置用Server模式,之下用Client模式
-
64位操作系统只有server模式
-
垃圾回收器
新生代
串行GC:Serial(Serial Coping)
一个单线程的收集器,在进行垃圾回收的时候,必须暂停其他所有的工作直到它收集完成。
对于单个CPU环境来说,没有线程交互的开销,可以获得最高的单线程垃圾回收效率。因此Serial依然是Java虚拟机运行在Client模式下的默认的新生代垃圾收集器。
对应的JVM参数:
-XX:+UseSerialGC
开启后:Serial(Young区使用)+ Serial Old(Old区使用)的收集器组合
新生代的是复制算法,老年代的是标记-整理算法
并行GC:ParNew
使用多个线程进行垃圾回收,在垃圾回收时,会暂停所有的工作线程直到它收集结束。
ParNew收集器就是Serial收集器新生代的并行多线程版本,它是很多Java虚拟机运行在Server模式下的新生代的默认的垃圾回收器。
对应的JVM参数:
-XX:+UserParNewGC
开启后:ParNew(Young区使用)+ Serial Old(Old区使用)的收集器组合
新生代的是复制算法,老年代的是标记-整理算法
并行GC:Parallel Scavenge
也是一个并行的新生代垃圾收集器,使用复制算法,称为吞吐量优先收集器。
它的重点关注:
可控制的吞吐量:吞吐量=用户程序运行时间/(用户程序运行时间+垃圾收集时间),高吞吐量意味着高效利用CPU的时间,它多用于后台运算而不需要太多交互的任务。
自适应调节策略:虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。
对应的JVM参数:
-XX:+UseParallerGC
-XX:+UseParallelOldGC
这两个参数可以互相激活。
开启后:新生代使用复制算法,老年代使用标记-整理算法
老年代
串行GC:Serial Old
是Serial垃圾收集器的老年代版本,同样是单线程收集器,使用标记-整理算法,也是默认应用在Client模式。
在Server模式下,主要有两个用途:
- 在JDK1.5之前,与新生代的Parallel Scavenge 收集器搭配使用
- 作为老年代中CMS收集器的后被垃圾收集器使用
并行GC:Parallel Old
Paralel Old 是Parallel Scavenge的老年代版本,使用标记-整理算法。JDK1.6才开始提供。
在JDK1.6之前,新生代使用Parallel Scavenge收集器只能搭配老年代的Serial Old收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量优先。Parallel Old就是在老年代提供了吞吐量优先的垃圾收集器。
对应的JVM参数:
-XX:+UseParallelOldGC
并发标记清除GC:CMS
是一种以获取最短回收停顿时间为目标的收集器,适用于互联网站或者B/S系统的服务器上,这类应用重视服务器的响应速度,CMS适合堆内存大、CPU核数多的服务端应用。
对应的JVM参数:
-XX:+UseConcMarkSweepGC
开启后:使用ParNew(Young区使用)+ CMS(Old区使用)+ Serial Old(Old区备用)的收集器组合
4个步骤:
- 初始标记:只是标记GC Roots能直接关联的对象,速度很快,仍然需要暂停其他所有线程
- 并发标记:进行GC Roots的跟踪过程,和用户线程一起工作,不需要暂停工作线程。标记全部对象
- 重新标记:修正正在并发标记期间,因用户线程继续运行而导致标记产生变动的那一部分对象,需要暂停其他线程,就是在正式清理前,再做修正。
- 并发清除:清除GC Roots不可达的对象,和用户线程一起工作,基于标记的结果,直接进行清除
优缺点:
- 并发收集停顿低
- 并发执行,对CPU资源压力大:由于并发执行,会增大对堆内存的占用,所以,CMS必须在老年代堆内存用尽之前完成垃圾回收,否则垃圾收集失败,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC
- 采用的标记清除算法导致大量的碎片:标记清除无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后不得不通过担保机制对对内存进行压缩。CMS也提供了参数-XX:CMSFullGCsBeForeCompaction来指定多少次CMS收集后进行一次压缩的FullGC(默认为0)。
垃圾收集器小总结
参数 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC | SerialGC | 复制 | SerialOldGC | 标记整理 |
-XX:+UseParNewGC | ParNew | 复制 | SerialOldGC | 标记整理 |
-XX:+UseParallelGC | Parallel[Scavenge] | 复制 | ParallelOld | 标记整理 |
-XX:+UseConcMarkSweepGC | ParNew | 复制 | CMS+SerialOld | 标记清除 |
-XX:+UseG1GC | 整体标记整理算法 | 局部复制算法 |
G1
之前的收集器的特点:
- 年轻代和老年代是各自独立且连续的内存块
- 年轻代收集使用单Eden+S0+S1进行复制算法
- 老年代收集必须扫描整个老年代区域
- 都是尽可能少而快速地执行GC为设计原则
G1的特点:
- 并行与并发
- 分代收集:整个内存分区不存在物理上的年轻代和老年代的区别,只有逻辑上的分代概念
- 空间整合:从整体上看采用标记整理算法;从局部上看采用复制算法,不会产生内存碎片
- 可预测的停顿:使用者可以明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾回收上的时间不超过N毫秒
区域化垃圾收集器:最大好处是化整为零,避免全内存区域扫描,只需要按照区域来进行扫描即可。
G1将整个堆内存划分为多个大小相等的独立区域(Region)(1M~32M),在JVM启动时会自动设置这些子区域的大小,G1不要求对象的存储一定是物理上连续的,只要是逻辑上连续的即可,每个分区也不会固定为某个代服务,可以按需求在年轻代和老年代之间切换。
4个步骤:
- 初始标记:只标记GC Roots能直接关联到的对象
- 并发标记:进行GC Roots Tracing的过程
- 最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
- 筛选回收:根据时间来进行价值最大化的回收
常用参数:
-XX:+UseG1GC
-XX:G1HeapRegionSize=n #设置G1区域的大小。值是2的幂,范围是1M~32M
-XX:MaxGCPauseMillis=n #最大GC停顿时间,这是个软目标,JVM尽可能(不保证)停顿小于这个时间
-XX:InitiationHeapOccupancyPercent=n #堆占用了多少的时候就出发GC(默认为45)
-XX:ConcGCThreads=n #并发GC使用的线程数
-XX:G1ReservePercent=n #设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险
G1和CMS比较:
- G1不会产生内存碎片
- G1可以精确控制停顿。
- G1把整个堆划分为多个固定大小的区域,每次允许根据停顿时间区收集最的区域