稀释剂的英文翻译英语怎么说-西点学习国家卫健委副主任
2023年4月19日发(作者:tramp)jvm专题系列——详解垃圾回收器、常⽤jvm参数、性能监控⼯具以及调优案例
⽂章⽬录
摘要
本篇博客接着讲解,看懂此篇博客需要你对jvm内存结构,垃圾回收机制和算法有⼀定了解,所以推荐没有了解的朋友可以先看⼀下上
篇jvm的博客,⼤神请⽆视。⾔归正传,有了充实的理论基础,便要开始运⽤于实践当中,本篇博客主要讲解jvm垃圾回收器的分类和选
择,常⽤jvm参数,性能监控⼯具以及调优实战,带你⼀步步揭开jvm的神秘⾯纱。
jvm垃圾回收器
古诗文网庄子 我们知道,jvm堆内存分为新⽣代和⽼⽣代,新⽣代采⽤复制算法,⽼⽣代采⽤标记-清除或者标记-整理算法来收集和清理垃圾,零落成泥碾作尘下一句 关于
算法的具体实现便是接下来要讲解的垃圾回收器。
jvm垃圾回收器⽬前主要有7种:serial收集器、parnew收集器、parallel scavenge收集器、serial old 收集器、parallel old收集器、
cms收集器、g1收集器。7种垃圾回收器做进⼀步划分可以分为:
:Serial、ParNew、Parallel曱甴的读音怎么读 Scavenge
新⽣代收集器
:CMS、Serial Old、Parallel Old
⽼⽣代收集器
: G1
整堆收集器
新⽣代收集器只能收集新⽣代的垃圾,⽼⽣代收集器只能收集⽼⽣代的垃圾,⽽整堆收集器G1新⽼通吃,值得⼀提的是,G1收集器将
整个Java堆划分为多个⼤⼩相等的独⽴区域(Region)。虽然也保留了新⽣代、⽼年代的概念,但新⽣代和⽼年代不再是相互隔离的,他
们都是⼀部分Region(不需要连续)的集合,所以G1收集器的堆内存结构跟我们之前介绍的结构区别是很⼤的,它标记了每个Region所
属的区域,然后再对其进⾏垃圾回收。
下图为jvm垃圾回收器图⽰:
如上图,连线表⽰可以搭配使⽤。你也许会好奇为什么cms不能和perallel scavenge搭配使⽤,cms为什么可以跟serial old搭配使
⽤,parallel old为什么只能和parallel sacvenge搭配使⽤。⾸先回答第⼀个问题:parallel scavenge没有使⽤其他Gc通⽤的Gc框架,导
致两者⽆法搭配,⾄于为什么没有使⽤同⼀个框架,这完全是⼈为原因,跟技术没有关系(也许未来可以实现两者共⽤吧)。第⼆个问题:
⼆者其实并⾮搭配使⽤,cms收集器(并发收集器)采⽤的是标记-清除算法,与⽤户线程并发运⾏,所以会产⽣浮动垃圾(标记完垃圾之
后产⽣的垃圾),当cms运⾏期间预留内存⽆法满⾜程序需要的时候,便会启动后备⽅案,使⽤serial old来进⾏收集(标记-整理),释放
内存空间。第三个问题:parallel old跟parallel scavenge⼀样,为并⾏收集器,不能跟seri《绝句》其二 al和parnew的原因同问题⼀。
在这⾥有⼀个概念:并⾏收集器和并发收集器,可以把⽤户线程作为参照物,跟⽤户线程⼀起运⾏称之为并发,没有⽤户线程参与称之
为并⾏。
Serial收集器
Serial收集器是最原始的⼀款垃圾收集器,也称之为串⾏收集器,顾名思义,它是单线程运⾏的,⽽且不⽌如此,它在收集垃圾的时
候,会暂停其他所有的⼯作线程,直到收集结束,被称之“stop the world”。想象⼀下,⽐如你在看电影,每看五分钟需要暂停⼏秒钟,
这显然是令⼈难以接受的。serial收集器的运⾏流程如下:
对于\"stop the world\"这种不良体验,虚拟机的开发者表⽰⾮常理解,但也⼼存委屈:“你妈妈为你打扫房间的时候,肯定会让你⽼⽼
实实坐着或者出去待着,如果⼀边打扫,你⼀边扔纸屑,那房间永远也⽆法打扫⼲净。”这听起来很有道理,并且事实的确如此,同时虚拟
机开发团队也在为消除或减少内存回收⽽导致的停顿⽽⼀直努⼒着!
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了使⽤多条线程收集垃圾之外,其他⾏为基本和Serial收集器的实现完全⼀样。
ParNew收集器只有在多核CPU的环境下才能发挥出它的优势(多线程收集速度快,停顿时间缩短),如果是单核CPU它甚⾄不如Serial收
集器的效果好(单核CPU的线程切换导致额外开销)。它的运⾏流程如下:
Parallel Scavenge收集器
Parallel Scavenge与ParNew类似,也是⼀款并⾏多线程收集器,相⽐于ParNew,它的⽬标则是达到⼀个可控制的吞吐量(吞吐量=
运⾏⽤户代码时间/(运⾏⽤户代码时间+垃圾收集时间)),如果虚拟机总共运⾏了100分钟,垃圾收集花了1分钟,那么吞吐量变为100-
1/100=99%。
停顿寄江州白司马情感赏析 时间越短越适合与⽤户进⾏交互的程序,良好的响应速度可以提升⽤户体验,⽽⾼吞吐量则是可以⾼效利⽤cpu,主要⽤于在后台
运算不需要进⾏⽤户交互的任务。
Parallel Scavenge提供了两个参数来控制吞吐量:-XX:MaxGCPauseMillis(控制停顿时间(jvm尽量不超过设置的时间),单位ms),-
XX:GCTimeRatio(吞吐量⼤⼩,⼤于0⼩于100),但你千万不要以为把停顿时间的参数设⼩,吞吐量参数设⼤就可以让垃圾收集的速度变
快,停顿时间的缩短是靠牺牲吞吐量和新⽣代空间来换取的:系统把新⽣代调⼩,⽐如由1000兆调节为700兆,收集700兆的空间速度必
然⽐明月几时有免费完整版 1000兆快,但是相应的收集频率会增⾼,原来10s收集⼀次,每次停顿100ms,现在需要5s收集⼀次,每次停顿70ms(相当于
10s停顿140ms),停顿时间确实下降了,但是吞吐量也降了下来。
所以,Parallel Scavenge也被称为“吞吐量优先收集器”,此收集器还有⼀个参数:-XX:+UseAdaptiveSizePolicy,打开这个参数
之后,不需要我们再额外设置新⽣代的⼤⼩以及新⽣代eden和survivor的⽐例等参数了,jvm会根据当前系统的运⾏情况动态调整这些参数
以提供最合适的停顿时间和吞吐量,这种调节⽅式称为GC⾃适应的调节策略,同时这也是Parallel Scavenge和ParNew的重要区别之⼀。
Serial Old收集器
Serial Old跟Serial⼀样是⼀个单线程收集器,使⽤“标记-整理”算法。他可以作为CMS收集器的后备军,运⾏流程同Serial收集器运
⾏流程。
Parallel Old收集器
Parallel Old收集器跟Parallel Scavenge⼀样是⼀个并⾏多线程收集器,采⽤“标记-整理”算法。它与Parallel Scavenge搭配适⽤于
增加吞吐量以及CPU资源敏感的场合。⼯作流程如下:
CMS收集器
CMS收集器全称Concurrent Mark Sweep,主打并发收集,低停顿,适⽤于B/S系统的服务端,我们熟知的淘宝⽹站使⽤的便是CMS
收集器,它的收集器线程可以跟⽤户线程⼀起⼯作,这也是与并⾏收集器所不同的地⽅,运⾏流程如下:
由上图可见,尽管CMS可以跟⽤户线程⼀起运⾏,但它同样也⽆法避免“stop the world”,之前的博客讲过可达性原则,即在初始
标记和重新标记验证对象死活的时候也会引起⼯作线程的停顿,只是停顿的时间较短。CMS收集器是⼀款⾮常优秀的垃圾回收器,但它也
存在以下缺点:
1.对CPU资源敏感。事实上,⾯向并发设计的程序对CPU资源都较为敏感,在并发阶段,他虽然不会使⽤户线程停顿,但是也会因为
占⽤了⼀部分CPU资源⽽使应⽤程序变慢,总吞吐量就会降低。
2.⽆法处理浮动垃圾,可能出现“Concurrent Mode Failure”导致另⼀次Full Gc(收集⽼⽣代成为Full Gc)。由于CMS在并发清理阶
段⽤户线程依然运⾏着并不断产⽣垃圾,这部分垃圾出现在重新标记之后,所以在本次Gc中⽆法清理,这部分垃圾就称为浮动垃圾。CMS
在垃圾收集的时候⽤户线程仍在运⾏,所以他不能向其他收集器⼀样等到⽼⽣代⼏乎填满再进⾏回收,需要预留⼀部分空间供并发时的程序
使⽤,可以通过:-XX:CMSInitIatingOccupancyFaction的参数值来调节触发收集的百分⽐,⼀般不需要特意动它。如果预留空间⽆法满
⾜程序运⾏的需要,那么就会出现Concurrent Mode Failure,这个时候就轮到Serial Old收集器登场了,jvm会临时使⽤Serial Old来重
新对⽼年代进⾏垃圾收集,这同时也就意味着系统停顿时间变长,所以此参数设置过⾼容易引起⼤量Concurrent Mode Failure,反⽽降
低性能!
3.产⽣⼤量内存碎⽚。CMS利⽤的是标记-清除算法来进⾏垃圾收集(⽐标记-整理快),这必然会不可避免的产⽣内存碎⽚,内存碎⽚
过多时,就算剩余空间很⾜,但是⽆法找到连续的内存空间去分配新来的⼤对象,就会不得不提前触发Full GC。我们可以通过开启
XX:UseCMSCompactAtFullCollection参数来解决此问题(默认开启),这样CMS在顶不住要进⾏Ful望月有感古诗 l GC时会对内存碎⽚进⾏合并整
理,但这也会使得停顿时间变长(内存整理⽆法并发执⾏)。通过XX:CMSFullGCsBeforeCompaction可以设置执⾏多少次不合并整理的
Full Gc后,执⾏⼀次带合并整理的Full Gc,默认为0,即每次进⼊Full Gc时都会进⾏碎⽚整理。
G1收集器
G1收集器是当今收集器技术发展的最前沿成果之⼀,它是⼀款⾯向服务端应⽤的垃圾收集器。
G1收集器具备以下特点:
1.并⾏和并发
2.分代收集
3.空间整合:从整体看它基于标记-整理算法,从局部(两个Region之间)来看则是基于复制算法,这意味着它在运⾏的时候不会产⽣
内存碎⽚,有利于程序长时间执⾏,不会因为分配⼤对象找不到连续的空间⽽提前触发Full Gc
4.可预测停顿:可以让使⽤者明确指定在⼀个长度为M毫秒的时间⽚段内,垃圾回收的时间不超过N毫秒。运⾏流程如下:
虽然G1收集器有诸多优点,但它的应⽤案例却少之⼜少,⽽且也缺乏与之相关的性能测试,但相信在未来G1会是最终的胜利者,我们
可以⼀直观望!如果你的收集器⽬前没有什么问题,那么⼤可以维持现状,如果你的应⽤追求的是吞吐量,那么G1并不会为你带来什么特
别的好处。
最好的垃圾回收器 ?
看到这⾥,我想你应该会明⽩不存在什么最好的垃圾回收器,选择什么回收器需要我们根据实际的业务场景来确定,如果追求低停顿,
可以考虑ParNew+CMS组合,如果追求⾼吞吐量,可以考虑Parallel Scavenge+Parallel Old组合,单核CPU下还可以考虑最经典的
Serial组合!
常⽤的jvm参数
通过jvm参数设置可以让我们实现对jvm的个性化定制,提⾼系统性能。使⽤参数只需要在java命令后⾯加上就可以,例如 java -
Xmx100m hello,在eliplse和idea中同样可以很⽅便的进⾏设置,设置⽅法⾃⾏百度。
堆参数
jdk8永久代已经废弃,替换为Metaspace(本地内存),相应参数可以⾃⾏百度,默认值⼤约为4096M,⼀般的应⽤来说⾜够了。堆
参数中可以适当把年轻代的内存设置的⼤⼀些,可以有效减少Full Gc的次数,提升系统的响应速度。
回收器参数
通过上表的参数可以指定使⽤的垃圾回收器,后⾯会介绍常⽤的回收器参数组合。
常⽤参数
如上表,后⾯的⼏个参数可以打印GC⽇志,另外通过Xloggc:log/可以指定gc⽇志的位置,查看垃圾回收的情况,同时在OOM
的时候可以在指定路径⽣成dump⽂件,⽅便我们可以使⽤性能监控⼯具分析查看。其中,xss参数值得注意,它是为每个线程所分配的内
存⼤⼩,⼀般来说不会超过2兆,所以,xss设置的越⼤,可运⾏的线程总数就越少,但相应的每个线程栈的深度也就越深,不容易发⽣栈
溢出,反之容易发⽣栈溢出,这也就是为什么有的公司严禁使⽤递归的原因,因为它会⼀直不停压栈。⼀般来说,jvm默认的⼤⼩基本已经
够⽤,不需要再特别去设置。
回收器常⽤组合
如上表,第⼆和第三种组合使⽤最为⼴泛!
性能监控⼯具
调优案例
由于环境限制,在本地复现问题相对来说⽐较困难,所以在这⾥主要通过第⼀⼈称故事的⽅式来进⾏讲解。
我们公司为客户做了⼀套数据利⽤系统供⽤户多维度查询数据并导出⽣成excel,下⾯是系统的具体配置:
服务器:centos7⼀台
内存:64G
jdk版本:1.8
web服务器:springboot内置tomcat
客户规模:⼀千⼈左右
讲道理,⼀千⼈使⽤,这种服务器配置可以说是⾮常奢侈了,所以感觉上是完全不会有问题的。但是后来客户却反应说偶尔系统在导出
excel的时候会有较长时间的卡顿,最长的时候甚⾄长达半分钟才能有所响应,于是便迅速介⼊调查。
⾸先考虑是不是sql有问题,但是卡顿的情况是偶尔发⽣,平时反应速度很快,所以暂时排除了这种可能。
然后去询问运维⼈员有没有进⾏服务器维护相关的操作,得到的答案是否定的,这就让⼈陷⼊了困扰之中。
没办法,这种问题只能从jvm上找原因了,于是使⽤了jvisualvm来进⾏系统监控,当我看到堆内存的时候吓了⼀跳,⾜⾜设置了
40g,后来经过询问,原来部署程序的哥们不想浪费宝贵的服务器资源,所以故意把堆内存设置的很⼤。相信我们都有优化eclipse或idea
的体验,默认的xmx⽐较⼩,当我们调⼤之后eclipse或idea的速度明显就快了很多,所以哥们想到了这⼀点,毫不留情的把系统堆内存设
置到了40g。
其实到这⾥问题已经⽐较明显了,⼤概率是进⾏Full GC的时候由于堆内存过⼤⽽需要耗费⼤量时间(stop the world)导致系统停顿
时间过长,因此⽤户便⽆法获得及时响应,后来我通过输出gc⽇志发现果然是Full GC引起的问题,40g的内存,⼀次消耗半分钟也不⾜为
奇。那么为什么会发⽣Full GC呢?原因其实很简单,客户在导出excel的时候⽣成的workbook对象需要封装⼤量的数据,所以属于⼤对
象,会被直接分配到⽼⽣代,随着时间推移,⼤对象越积越多,⽼⽣代内存不够⽤时,⾃然要触发Full GC。值得⼀提的是,如果确认程序
中不会有⼤对象的产⽣,那么可能对象都没有进⼊⽼⽣代的机会,这种场景下主要在新⽣代中进⾏垃圾回收(minor gc),速度是⾮常快
的,系统就会“飞起来”,但⼀般不会有这种理想的情况。
卡顿的原因定位了,优化的⽅法也显⽽易见,最简单的⽅法:调⼩堆内存即可!后来除了调⼩堆内存,还做了以下优化:
1.调⼩堆内存到4g
2.单机部署6个节点,使⽤nginx负载均衡,提⾼cpu的利⽤率
3.使⽤ParNew+CMS收集器组合(jdk默认为Parallel Sca苦字组词大全 venge+Parallel Old组合)
优化过后,问题成功得到了解决。
通过此案例也许会对你有所启发,很多⼈认为单机部署⼀个应⽤程序独占⼀台服务器是最好的选择,因为这样不会有其他程序抢占cpu
资源,实际上这也不是绝对的,具体情况还需要具体分析。另外如果对jvm不够了解,不要贸然设置参数,否则可七月七日是情人节吗 能会留下很⼤的坑,默认
的往往不会有太⼤的问题。
说到底,jvm调优只是⼀个辅助⼿段,⼤部分情况下往往都是代码层⾯的问题,通过优化代码,擅⽤设计模式,优化数据库结构,合理
建⽴索引等⽅式照样会让系统稳定流畅运⾏。
⼩结
本篇博客的内容到这⾥就结束了,希望能对你有所启发,之后我会介绍jvm类加载器相关的知识并且⼿动实现⼀个简易的热部署插件。
感谢您的观看,再见!
resistant是什么意思istant在线翻译读音-国际学校有哪些
更多推荐
scavenge是什么意思venge在线翻译读音例
发布评论