jvm性能调优 - 13JVM的年轻代垃圾回收器ParNew原理

简介: jvm性能调优 - 13JVM的年轻代垃圾回收器ParNew原理

Pre

前面的文章已经给大家把整个JVM的核心运行原理全部梳理清楚了,大家现在应该对以下问题非常的清晰明了:

  • 对象在新生代分配,然后什么时候会触发Minor GC
  • 触发Minor GC之前会如何检查老年代可用内存大小和新生代对象大小,如何检查老年代可用内存大小和历次Minor GC之后升入老年代的平均对象大小
  • 什么情况下Minor GC之前会提前触发Full GC,什么情况下会直接触发Minor GC
  • Minor GC之后有哪几种情况对象会进入老年代

而且大家也大概知道了垃圾回收器、垃圾回收线程、垃圾回收算法之间的关系,包括垃圾回收的过程中的“Stop the World”现象和场景对系统运行性能的影响。

接着我们就要来相对深入的研究一下常见的新生代和老年代的垃圾回收器的运行原理了,同时看看常见的垃圾回收参数一般会怎么来设置。

同时结合案例来研究一下在你上线一个新系统的时候,如何通过预估的手段和方法提前对系统的垃圾回收参数进行合理的设置。


GC回收器概述

在新生代和老年代进行垃圾回收的时候,都是要用垃圾回收器进行回收的,不同的区域用不同的垃圾回收器。 到时候会深入分析我们常用的ParNew、CMS和G1三种垃圾回收器的工作原理和优缺点。

这篇文章先简单给大家介绍一下:

  • Serial和Serial Old垃圾回收器

工作原理就是单线程运行,垃圾回收的时候会停止我们自己写的系统的其他工作线程,让我们系统直接卡死不动,然后让他们垃圾回收,这个现在一般写后台Java系统几乎不用。

  • ParNew和CMS垃圾回收器

ParNew现在一般都是用在新生代的垃圾回收器,CMS是用在老年代的垃圾回收器,他们都是多线程并发的机制,性能更好,现在一般是线上生产系统的标配组合。着重分析这两个垃圾回收器。

  • G1垃圾回收器

统一收集新生代 和老年代,采用了更加优秀的算法和设计机制,重点理解G1垃圾回收器的工作原理和优缺点


最常用的新生代垃圾回收器:ParNew

一般来说,在之前多年里,假设没有最新的G1垃圾回收器的话,通常大家线上系统都是ParNew垃圾回收器作为新生代的垃圾回收器

当然现在即使有了G1,其实很多线上系统还是用的ParNew。

通常运行在服务器上的Java系统,都可以充分利用服务器的多核CPU的优势,所以大家可以想一下,假设你的服务器是4核CPU,如果对新生代垃圾回收的时候,仅仅使用单线程进行垃圾回收,是不是会导致没法充分利用CPU资源?

如下图:

比如上图,现在你在垃圾回收的时候,都把系统程序所有的工作线程全部停掉了,就一个垃圾回收线程在运行

那么此时4核CPU的资源根本没法充分利用,理论上4核CPU就可以支持4个垃圾回收线程并行执行,可以提升4倍的性能!

所以说,新生代的ParNew垃圾回收器主打的就是多线程垃圾回收机制,另外一种Serial垃圾回收器主打的是单线程垃圾回收,他们俩都是回收新生代的,唯一的区别就是单线程和多线程的区别,但是垃圾回收算法是完全一样的。

大家看下图,ParNew垃圾回收器如果一旦在合适的时机执行Minor GC的时候,就会把系统程序的工作线程全部停掉,禁止程序继续运行创建新的对象,然后自己就用多个垃圾回收线程去进行垃圾回收,回收的机制和算法就跟之前说的是一样的。


如何为线上系统指定使用ParNew垃圾回收器?

一般来说,对于线上系统部署启动的时候,我们之前都看过多种方式来设置JVM参数了,在Eclipse/IntelliJ IDEA中可以设置Debug JVM Arguments,使用“java -jar”命令启动时直接在后面跟上JVM参数即可

部署到Tomcat时可以在Tomcat的catalina.sh中设置Tomcat的JVM参数,使用Spring Boot也可以在启动时指定JVM参数。

那么在启动系统的时候如果要指定使用ParNew垃圾回收器,是用什么参数呢?

很简单,使用“-XX:+UseParNewGC”选项,只要加入这个选项,JVM启动之后对新生代进行垃圾回收的,就是ParNew垃圾回收器了。

那么Minor GC的时机,检查机制,包括垃圾回收的具体过程,以及对象升入老年代的机制,都是我们之前说过的那套原理了,只不过大家要知道,ParNew会使用多个线程来进行垃圾回收。


ParNew垃圾回收器默认情况下的线程数量

因为现在一般我们部署系统的服务器都是多核CPU的,所以为了在垃圾回收的时候充分利用多核CPU的资源,一旦我们指定了使用ParNew垃圾回收器之后,他默认给自己设置的垃圾回收线程的数量就是跟CPU的核数是一样的。

比如我们线上机器假设用的是4核CPU,或者8核CPU,或者16核CPU,那么此时ParNew的垃圾回收线程数就会分别是4个线程、8个线程、16个线程

这个东西一般不用我们手动去调节,因为跟CPU核数一样的线程数量,是可以充分进行并行处理的。

比如下图,大家可以看到,每个线程都通过一个CPU在运行。

但是如果你一定要自己调节ParNew的垃圾回收线程数量,也是可以的,使用“-XX:ParallelGCThreads”参数即可,通过他可以设置线程的数量

但是建议一般不要随意动这个参数,如果要优化,具体结合后续的案例我们给大家展开。


思考

  • 到底是用单线程垃圾回收好,还是多线程垃圾回收好?
  • 到底是Serial垃圾回收器好还是ParNew垃圾回收器好?

对这个问题要给大家略微展开做点解释。

启动系统的时候是可以区分服务器模式和客户端模式的,如果你启动系统的时候加入“-server”就是服务器模式,如果加入“-cilent”就是客户端模式。

他们俩的区别就是,如果你的系统部署在比如4核8G的Linux服务器上,那么就应该用服务器模式,如果你的系统是运行在比如Windows上的客户端程序,那么就应该是客户端模式。

那么服务器模式和客户端模式的区别是啥呢?

服务器模式通常运行我们的网站系统、电商系统、业务系统、APP后台系统之类的大型系统,一般都是多核CPU

所以此时如果要垃圾回收,那么肯定是用ParNew更好,因为多线程并行垃圾回收,充分利用多核CPU资源,可以提升性能。如下图。

反之如果你部署在服务器上,但是你用了单线程垃圾回收,那么就有一些CPU是被浪费了,根本没用上

比如下图。

那么如果你的Java程序是一个客户端程序,比如类似百度云网盘的Windows客户端,或者是印象笔记的Windows客户端,运行在Windows个人操作系统上呢?

这种操作系统很多都是单核CPU,此时你如果要是还是用ParNew来进行垃圾回收,就会导致一个CPU运行多个线程,反而加重了性能开销,可能效率还不如单线程好

因为单CPU运行多线程会导致频繁的线上上下文切换,有效率开销,如下图。

所以如果是类似于那种运行在Windows上的客户端程序,建议采用Serial垃圾回收器,单CPU单线程垃圾回收即可,反而效率更高,如下图。

但是其实现在一般很少有用Java写客户端程序的,几乎很少见,Java现在主要是用来构建复杂的大规模后端业务系统的,所以常见的还是用“-server”指定为服务器模式,然后配合ParNew多线程垃圾回收器。

但是大家还是应该清楚单线程和多线程对垃圾回收的适用场景。


面试题,parnew+cms的gc,如何保证只做ygc,jvm参数如何配置

简单列举一下

  • 加大分代年龄,比如默认15加到30;
  • 修改新生代老年代比例,比如新生代老年代比例改成2:1
  • 修改e区和s区比例,比如改成6:2:2

对于这个面试题,如果前面的知识理解到位了,JVM原理和案例,完全可以把这个面试题回答的滴水不漏

结合案例和画图给面试官说明,而不是干巴巴的简单给几个方法,可以回答的有血有肉,让面试官无话可说。

其实要做到仅仅young gc,而几乎没有full gc是不难的,只要结合自己系统的运行,根据他的内存占用情况,GC后的对象存活情况,合理分配Eden、Survivor、老年代的内存大小,合理设置一些参数,即可做到。


相关文章
|
6月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
557 55
|
8月前
|
Prometheus 监控 算法
CMS圣经:CMS垃圾回收器的原理、调优,多标+漏标+浮动垃圾 分析与 研究
本文介绍了CMS(Concurrent Mark-Sweep)垃圾回收器的工作原理、优缺点及常见问题,并通过具体案例分析了其优化策略。重点探讨了CMS的各个阶段,包括标记、并发清理和重标记
CMS圣经:CMS垃圾回收器的原理、调优,多标+漏标+浮动垃圾 分析与 研究
|
8月前
|
存储 算法 Java
G1原理—5.G1垃圾回收过程之Mixed GC
本文介绍了G1的Mixed GC垃圾回收过程,包括并发标记算法详解、三色标记法如何解决错标漏标问题、SATB如何解决错标漏标问题、Mixed GC的过程、选择CollectSet的算法
G1原理—5.G1垃圾回收过程之Mixed GC
|
6月前
|
缓存 算法 Java
JVM深入原理(八)(一):垃圾回收
弱引用-作用:JVM中使用WeakReference对象来实现软引用,一般在ThreadLocal中,当进行垃圾回收时,被弱引用对象引用的对象就直接被回收.软引用-作用:JVM中使用SoftReference对象来实现软引用,一般在缓存中使用,当程序内存不足时,被引用的对象就会被回收.强引用-作用:可达性算法描述的根对象引用普通对象的引用,指的就是强引用,只要有这层关系存在,被引用的对象就会不被垃圾回收。引用计数法-缺点:如果两个对象循环引用,而又没有其他的对象来引用它们,这样就造成垃圾堆积。
171 0
|
6月前
|
算法 Java 对象存储
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
126 0
|
8月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
8月前
|
存储 监控 架构师
ZGC圣经:ZGC垃圾回收器的原理、调优,ZGC 漏标的 分析与 研究
ZGC圣经:ZGC垃圾回收器的原理、调优,ZGC 漏标的 分析与 研究
|
8月前
|
存储 缓存 算法
G1原理—3.G1是如何提升垃圾回收效率
本文深入探讨了G1垃圾回收器提升GC效率的核心机制,包括记忆集(RSet)、位图(BitMap)和卡表(CardTable)的设计与作用。记忆集通过记录跨代引用避免了不必要的老年代遍历,位图用于高效描述内存使用状态以优化标记过程,而卡表则在节约记忆集内存的同时提供更详细的引用信息。此外,文章还解析了DCQ(Dirty Card Queue)和DCQS(Dirty Card Queue Set)机制如何异步更新RSet,确保在高并发场景下的性能与准确性。这些设计共同提升了G1在标记、清理及整理内存时的效率。
321 10
|
8月前
|
存储 算法 Java
G1原理—6.G1垃圾回收过程之Full GC
本文详细探讨了G1垃圾回收器对Full GC(FGC)的优化处理,涵盖FGC的前置处理、整体流程及并行化改进。重点分析了传统FGC串行化的局限性以及G1通过Region分区和RSet机制实现并行标记的优势,包括任务窃取提升效率、跨分区压缩以生成空闲Region等技术细节。此外,文章还介绍了G1的新特性——字符串去重优化,通过判断char数组一致性减少重复字符串占用内存,从而提升内存使用效率。总结部分全面回顾了G1在FGC中的各项优化措施及其带来的性能改善。
G1原理—6.G1垃圾回收过程之Full GC
|
8月前
|
存储 算法 Java
G1原理—4.G1垃圾回收的过程之Young GC
本文详细解析了G1垃圾回收器中YGC(Young Generation Collection)的完整流程,包括并行与串行处理阶段。内容涵盖YGC相关参数设置、YGC与Mixed GC及FGC的关系、新生代垃圾回收的具体步骤(如标记存活对象、复制到Survivor区、动态调整Region数量等),以及并行阶段的多线程操作和串行阶段的关键任务(如处理软引用、整理卡表、重构RSet)。
G1原理—4.G1垃圾回收的过程之Young GC