JVM学习笔记之二

Stella981
• 阅读 485

    上一篇博文记录了gc的各种算法,这篇博文记录HotSpot中的几种垃圾回收器。垃圾回收器是不同虚拟机对算法思想的实现。

JVM学习笔记之二

    这张图上面三个是新生代回收器、下面三个是老年代收集器。连线表示两者可以配合使用,例如在虚拟机中可以新生代使用Serial,老年代使用CMS。下面会介绍各种回收器的特性、原理和使用场景。收集器并没有好坏之分,只有是否适合应用的场景。

一、新生代收集器

1、Serial

    顾名思义,这个收集器是“连续”的(单个GC线程)。意味着新生代执行gc时所有正常的工作线程是挂起状态。Serial是最基础的收集器。随着虚拟机的发展出现了后面的收集器,但也有其优势。简单高效,对于单核环境,没有线程交互的开销,并且适合运行在Client模式下的虚拟机。java -client, java -server可以控制jvm模式。

2、ParNew

    ParNew其实就是Serial的多线程版本,除了使用多线程垃圾收集器之外(多个GC线程),其余行为包括控制参数、收集算法、STW、对象分配规则、手机策略都与Serial相同。ParNew是server模式虚拟机首选的收集器,因为目前只有它能与CMS配合使用。ParNew适合在多核的服务器上使用。

3、Parallel Scavenge(PS)

    字面意思是“并行清理”,如图可知PS也是新生代收集器、多线程、也使用了复制算法。PS的关注点是吞吐量自适应。这是与前面收集器不同的。

    在这里吞吐量被定义为cpu用于运行用户代码的时间与cpu总时间之比。首先我想简单说一下我对吞吐量的理解。吞吐量的定义是单位时间传输、处理的字节数或其他单位。吞吐量大不一定意味着性能好或可用率高。对于gc来说cpu的吞吐量高意味着STW的时间会更长。对于要求响应速度的系统,如果有1分钟无法提供服务,是完全不合理的。选择吞吐量小但响应速度快的,可能是更好的方案。两者的区别是,后者可能是顿卡,但可以提供服务,前者是在某段时间内无法提供服务,但其他时间正常。前面可能会得出吞吐量大性能差这个结论,其实不然,吞吐量即效率,能响应更多的请求。

    所以结论是:停顿时间越短越适合与用户交互的程序,能够提升用户体验,而高吞吐量则可以高效利用cpu时间,尽快完成运算任务,主要适合后台运算而无需交互的任务。

    PS提供了两个参数用于精确控制吞吐量:最大停顿时间-XX:MaxGCPauseMillis以及吞吐量大小-XX:GCTimeRatio。GC的停顿时间缩短是牺牲了吞吐量和新生代空间,收集300M和500M新生代,肯定是前者快,但触发的频率高,假设后者是10s收集一次,每次100ms,前者是5s收集一次,每次50ms。收集速度加快,但效率变低了。

    PS收集器还有一个参数-XX:+UseAdaptiveSizePolicy。字面意思是“使用自适应的大小”,他的作用也是如此。当参数打开,就不需要指定新生代的小小、Eden和Survivor的比例等细节参数,虚拟机会根据当前系统运行情况收集性能监控信息,动态调整参数,根绝前面两个参数定义的最大停顿时间和吞吐率提供最合适的停顿时间或最大的吞吐量。

二、老年代收集器

1、Serial Old

    单线程老年代收集器,使用标记-整理算法。可以应用在client模式下的虚拟机。如果是server模式,1.可以跟PS配合使用 2.作为CMS收集器的后备方案,在并发收集产生Concurrent Mode Failure时使用,也是两者连线的原因。

2.Parallel Old

    多线程老年代收集器,同样适用标记-整理算法。在Parallel Old出现之前,新生代的PS只能与Serial Old配合使用。尴尬的是,这种组合并不能体现出PS在吞吐量上的优势。在多核环境中其综合性能不如ParNew+CMS。Parallel Old与Parallel Scavenge的组合适合应用于注重吞吐量和cpu资源敏感的场景中。这里还需要再次说明,以上不管是单线程还是多线程都会造成STW,所有工作线程都是挂起状态。

3.CMS

    Concurrent Mark Sweep 是一种以获取最短回收时间为目标的收集器。Mark Sweep -> 标记-清除算法,整个过程分为4步

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清除

    初始标记和重新标记任然需要STW。初始标记仅标记GC Root直接关联的对象,速度快,单线程。并发标记进行GC Roots Tracing的过程。重新标记是为了标记并发标记期间发生变化的对象,多线程。

    由于时间最长的并发标记、并发清除都是与工作线程并行的,且其余两个过程STW的时间相比并发阶段要远远小于,所以总体上来说CMS的清除过程是与用户线程并发的。

    制约CMS的条件也有很多:

  1. 因为与用户工作线程并发,毫无疑问会降低系统的并发量。CMS默认使用(cpu数量+3)/4个线程,意味着至少25%的cpu资源是用于gc的。
  2. 由于在清理过程中还会继续产生新的“垃圾”,这些垃圾只能留到下次清理,这些垃圾成为“浮动垃圾”。因此CMS不会在老年代快用完时触发,需要留一部分空间给“浮动垃圾”。虚拟机中可以通过-XX:CMSInitiatingOccupancyFraction控制空间大小。如果参数设置过高,预留空间少,则容易出现老年代无法满足需求的情况,此时就会出现一次Concurrent Mode Failure。还记得前面说的Serial Old可以配合CMS使用吗,此时就会启动这种后备方案。这样停顿时间就很长了。
  3. CMS使用标记-清除算法,无疑会增加内存碎片。我猜测不使用标记-整理算法的原因是尽量缩短STW。如果没有足够的空间分配大对象就会提前触发一次Full GC。CMS提供了-XX:+UseCMSCompactAtFullCollection的开关参数,用于没有足够空间的情况下是否做内存碎片整理的操作。默认开启。还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,用于设置执行多少次Full GC后执行一次压缩整理操作。默认是0。这点其实很尴尬,收集操作很难做到两全,就像cap理论,保证一致性就会影响可用性,保证可用性就要牺牲一致性。

    说了这么多都是纸上谈兵,但在上一篇博文中也说明过了,必然会有很多理论上的知识。至于收集器是如何标记、如何清除内存、如何整理等等,也许作为一个初学者或应用系统工程师会很难接触到,作为初学者的我还没有那么深的理解。但是如果把以上只是都掌握,对于运维自己开发的系统是很有帮助的。

ps:上面又是只说了GC没说是Young GC 还是 Full GC,请按不同年代的收集器理解就好。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这