GC类别
Minor GC :清扫实力派,Minor GC是最频密促发的GC,速率也最慢的,主要组织工作基本原理是:
第一类在young区的eden建立,当eden内部空间届满时促发Minor GC,将还生存的第一类导入到两个survivor0中,另两个survivor1也会将第一类拷贝过去,然后对eden和survivor1展开全部清扫,survivor0和survivor1就这样不断交错,浑然不觉两个是空着的,当第一类放不下或者是第一类年纪足够多老(预设15)会将其放进Old区,由此看来Minor GC时不仅会清扫第一类,还会将第一类放进Old区。能透过增设-XX:MaxTenuringThreshold=n来选定第一类经过多少次Minor GC后就进入Old 区,预设15
能透过增设-XX:SurvivorRatio=n增设Survivor区和eden区的比率,如-XX:SurvivorRatio=8 那么比率是8:1:1 ,没有增设的话就以-XX:InitialSurvivorRatio=n为预设增设,那个值预设是8Major GC :清扫老二十世纪,Major GC比Minor GC慢五倍以上,原则上促发Major GC只有CMS和G1有那个能力,因为cms和G1处理老二十世纪不须要促发full gc,其他的老二十世纪拆解器拆解老二十世纪须要促发full gc。Full GC: 清扫整座堆内部空间,包括实力派、永久性代和老二十世纪,它会开启老二十世纪过滤器和实力派过滤器一起组织工作,不间断Stop The World,尽可能减少Full GCFull gc:
full gc是对实力派,老二十世纪、永久性久代的统一拆解,虽然是对整座内部空间的拆解,并且会触发控制系统的停滞(stop-the-world),因此应尽可能的减少控制系统full gc的单次。
促发的full gc的几个条件
老二十世纪内部空间严重不足:实力派假如耗电量严重不足会将第一类放在老二十世纪,老二十世纪内部空间严重不足是会促发full gc,透过-Xmn增设实力派大小不一,也能透过-XX:NewRatio=n增设比率,预设值是2,老二十世纪:实力派=》2:1永久性代内部空间严重不足:永久性代在jdk7是存放在堆中的,能透过-XX:PermSize=n和-XX:MaxPermSize=n增设大。
在jdk8中叫作元内部空间,使用的是计算机控制系统的邻近地区缓存透过-XX:MaxMetaspaceSize=n增设大小不一,假如不增设,预设最大缓存大小不一是计算机控制系统的邻近地区缓存,虽然运转时自变量池在方式区中,而永久性代又是方式区的实现,所以运转时自变量池随着jdk8也终端到了邻近地区缓存,但是不论是jdk7却是jdk8,数组自变量池却是在堆中,数组自变量的建立是须要耗用堆缓存的CMS 产生碎块过多已经扛不住压力了就会初始化full gc展开资源整合:能透过-XX:CMSFullGCsBeforeCompaction=n增设要继续执行无数次full GC才会做填充。预设是0,也是每天full gc时就会对内部空间碎块展开重新整理,在预设实用性下假如碎块过多CMS GC扛不住了,要是转至full GC的时候单厢做填充。(假如Full GC比较频密,那么就不能每天都重新整理缓存内部空间,不然积少成多,停滞的时间也是很可观的,此时要是调大该参数,让CMS在经过多次Full GC后再对缓存内部空间展开填充重新整理,而假如Full GC发生的不频密,间隔时间较长,就能增设成每天Full GC后单厢对缓存内部空间进行填充重新整理,影响也不大。)CMS GC时出现了promotion failed和concurrent mode failure:
1、promotion failed意思是晋升失败是虽然实力派把一些第一类往老二十世纪扔,然后老二十世纪内部空间严重不足则抛出“promotion failed”,促发full gc,可能的原因是:Survivor内部空间过小或者老二十世纪内部空间小或者碎块多,或者两者同时发生2、concurrent mode failure是CMS增设开启的老二十世纪缓存占比阈值过高,所以导致控制系统无法预留足够多的内部空间满足程序需求,就会出现concurrent mode failure,开启担保机制,老二十世纪增长过快促发full gc展开清扫,解决方式是降低促发CMS的阀值,使用-XX:CMSInitiatingOccupancyFraction调低阈值,预设值是68,能调到50统计得到实力派minor gc时晋升到老二十世纪的平均大小不一大于老生代剩余内部空间:
原因有几个:1、代码问题大量大第一类直接进入老二十世纪 2、老二十世纪内部空间严重不足,透过-XX:NewRatio=n能调整老二十世纪和年轻的堆比率代码直接初始化System.gc()会建议控制系统初始化full gc:在GC日志中会显示为[Full GC(System),能开启-XX:-DisableExplicitGC禁止此类full gcGC日志
透过增设控制系统参数:-XX:+PrintGCDetails能GC日志打印到控制台,以下几个命令能增设GC日志
实战:
image.png
运转这段代码,虽然堆缓存被这些自变量占满,马上就会促发gc了
打印了下列的内容,我把它们一部分拷贝出来
image.png
上面的有三部分我用虚线标出来了行中
第一部分:我使用了-XX:+PrintCommandLineFlags命令打印的部分jvm参数信息:初始化堆大小不一,最大堆大小不一等信息,还有-XX:+UseParallelGC使用的是Parallel Scavenge过滤器第二部分(重点):非full gc 信息1、PSYoungGen:实力派GC区域的信息,那个实力派区域的名字会跟着过滤器的不同而改变,ParNew过滤器叫作ParNew,Serial叫作DefNew,Parallel Scavenge叫作PSYoungGen,虽然我使用的是Parallel Scavenge过滤器所以实力派显示为PSYoungGen
2、[PSYoungGen:488K->441K(1024K)]:“GC前该缓存区域已使用耗电量->GC后该缓存区域已使用耗电量(该缓存区域可用总耗电量,这里Eden+Survivor0,另外两个虽然是展开拷贝用的,所以不计入耗电量)”
3、505K->488K(1536K), 0.0006483 secs:“GC前Java堆已使用耗电量->GC后Java堆已使用耗电量(Java堆总耗电量),“0.0006483 secs”表示该缓存区域GC所占用的时间,单位是秒”
4、[Times: user=0.00 sys=0.00, real=0.00 secs]:这里面的user、sys和real与Linux的time命令所输出的时间含义一致,分别代表用户态耗用的CPU时间、内核态耗用的CPU事件和操作从开始到结束所经过的墙钟时间(Wall Clock Time)。CPU时间与墙钟时间的区别是,墙钟时间包括各种非运算的等待耗时,例如等待磁盘I/O、等待线程阻塞,而CPU时间不包括这些耗时,但当控制系统有多CPU或者多核的话,多线程操作会叠加这些CPU时间,所以读者看到user或sys时间超过real时间是完全正常的第三部分(重点):full gc 信息促发full gc会严重影响程序的运转性能,因为发生了牛批的stop-the-world
1、PSYoungGen:实力派GC区域发生了full gc
2、ParOldGen:发生老二十世纪的区域,虽然过滤器不同老二十世纪的区域名称也会不同,Serial old叫作Tenured,Parallel Old叫作ParOldGen,虽然我使用的是Parallel Scavenge过滤器会自动激活Parallel Old过滤器所以老二十世纪叫作ParOldGen
3、Metaspace:元内部空间,jdk7叫作Perm也是方式区也成为永久性代,jdk8后把方式区移除了,变成了元内部空间也是那个Metaspace下面列出gc日志中各个实力派过滤器预设配合的老二十世纪过滤器及各自对应的实力派区域和老二十世纪区域的名字:
1、Serial:DefNew ——> Serial Old:Tenured2、ParNew:ParNew ——> Serial Old:Tenured3、Parallel Scavenge:PSYoungGen ——> parNew Old:ParOldGen4、CMS是比较特殊的使用的标记清除法,只能ParNew、Serial配合,一般是选择ParNew与CMS配合,CMS是多线程的老二十世纪过滤器有很低的停滞时间,所以性能也比较不错,可惜它不能和Parallel Scavenge配合,所以CMS不是预设使用的过滤器,要使用必须自己配,选择了CMS就会预设开启ParNew和Serial Old,开启担保机制,后面我会专门讲它的5、G1是老少通吃的,不用和其他过滤器配合,并且性能高停滞时间少,到jdk9后是预设的过滤器,越到后面越牛批这是目前官方推荐的配合主流
image.png
控制台这玩意在线上能看?试一下生产日志文件?
image.png
image.png
打开后长这样:
image.png
GC过滤器
并发:过滤器和用户程序同时展开并行:暂停用户程序使用多线程继续执行GCSerial
Serial是串行实力派过滤器,client的预设过滤器,Serial Old是老二十世纪的Serial,这两个特点除了组织工作区域与算法不一样其他没啥区别,一般适用于小型应用和单处理器,单核GC效率较高,但是会发生Stop-The-World,能与CMS,Serial Old使用,虽然没有线程开销,所以在单核情况下性能无敌(现在没什么服务器是单核了吧!!)
-XX:+UseSerialGC:如何没有实用性其他的老二十世纪,那个实用性能同时打开Serial和Serial Old,否则只打开SerialSerial会发生Stop-The-World
ParNew(jdk9已经不支持)
是Serial的多线程版,本并行过滤器,除了多线程与Serial没什么区别,这两款是共用了绝大部分的代码,所以特点几乎没什么区别,一样会发生Stop-The-World。许多Server选它只是为了和CMS配合而已。(如何不是为了配合CMS最好不要用那个过滤器)
-XX:+UseParNewGC:如何没有实用性其他的老二十世纪,那个实用性能同时打开ParNew和Serial Old,否则只打开ParNew-XX:ParallelGCThreads=n:增设并行过滤器收集时使用的线程数,假如cpu核数小于8最好与CPU数目相等,假如大于8能增设为3+(cpu core*5)/8。依然会发生Stop-The-World
Parallel Scavenge
并行过滤器,那个过滤器比较重要,是jdk7和jdk8 Server端预设的实力派过滤器,Parallel Scavenge的特点是多线程,追求高吞吐量(吞吐量是假如控制系统运转100分钟,过滤器组织工作1分钟,吞吐量为99%),它有自适应调节的功能,透过收集控制系统的信息动态调节自身的参数,达到高吞吐的目的,吞吐高代表CUP使用率高,优点很明显但是缺点也很明显,它无法与老二十世纪的CMS适配,同样是高性能的CMS追求的是最短的停滞时间,鱼和熊掌不可兼得,jvm却是选择了Parallel Scavenge作为预设Server的过滤器,为了配合它的高吞吐,jvm还专门弄了两个ParNew Old,它是ParNew的老年版本。(这么牛批但是依然会发生Stop-The-World)
-XX:+UseParallelGC:透过那个实用性,能同时打开Parallel Scavenge和ParNew Old-XX:ParallelGCThreads=n:增设并行过滤器收集时使用的线程数,假如cpu核数小于8最好与CPU数目相等,假如大于8能增设为3+(cpu core*5)/8。-XX:MaxGCPauseMillis=n:增设实力派每天并行垃圾拆解的最大暂停时间。增设那个值拆解器会尽可能的去实现,jvm会根据那个停滞时间,把扫描的范围缩小成两个小的堆展开GC扫描达到缩短停滞的目的-XX:GCTimeRatio=n:增设垃圾拆解时间占程序运转时间的百分比。公式为1/(1+n)。预设是99-XX:+UseAdaptiveSizePolicy:自适应策略,自动选择实力派区大小不一和相应的Survivor区比率、吞吐量(GCTimeRatio),停滞时间(MaxGCPauseMillis),那个是在Listary比较复杂的场合下能使用-XX:+ScavengeBeforeFullGC:在full gc前促发一次minor gcCMS(jdk9已经不建议使用,被G1取代)
那个比较牛批的老二十世纪过滤器,使用的是标记清除法,虽然追求的是最短停滞时间,所以可以带给用户良好的体验,非常适合互联网和B/S的服务器。
一般老二十世纪和永久性代的拆解是须要促发full gc的,但是CMS能在不促发full gc的情况下原则上对老二十世纪和永久性代展开gc,但是它的gc是须要检查永久性代和老二十世纪内部空间使用率预设是达到92%才开启,还要透过-XX:+
CMSInitiatingOccupancyFraction=n增设,gc有7个步骤1、初始标记(STW initial mark),会Stop-The-World
2、并发标记(Concurrent marking),并发操作,不会Stop-The-World
3、并发预清扫(Concurrent precleaning),并发操作,不会Stop-The-World
5、重新标记(Final remark),会Stop-The-World
6、并发清扫(Concurrent sweeping),并发操作,不会Stop-The-World
7、并发重置(Concurrent reset),并发操作,不会Stop-The-World我们看到只是标记的部分都是会发生Stop-The-World的,但是标记的操作是非常短的,能忽略不计的,所以CMS能做到最短的停滞时间,效率极高,但是缺点是无法与Parallel Scavenge适配,CMS开启时会同时预设开启ParNew、Serial Old,为了防止SMC的拆解失败,使用了CMS+ParNew+Serial Old的担保机制
担保机制:
虽然CMS为了降低停滞,有一定的并发,开启时须要预留足够多的缓存给用户线程,所以CMS须要在缓存内部空间满之前就得开启,jdk5预设是当老二十世纪65%时开启,而jdk6以后是预设92%,还要透过-XX:+
CMSInitiatingOccupancyFraction增设更高的启百分比,假如垃圾生产的太快了或者阈值太高,在CMS运转期间发现无法预留足够多的缓存,就会出现concurrent mode failure而运转异常,这时JVM就会开启备用的Serial Old去处理老二十世纪垃圾,所以CMS+ParNew+Serial Old的担保机制是为了防止CMS的concurrent mode failure的异常-XX:+UseConcMarkSweepGC:使用此命令能同时开启CMS、ParNew、Serial Old。-XX:ParallelCMSThreads=n: 设定 CMS 的并发线程数量。预设线程数是(ParallelGCThreads+3)/4。意味着当预设条件下ParallelGCThreads为4时,只有两个线程并发-XX:+CMSParallelRemarkEnabled:采用并行标记方式降低Stop-The-World的时间。-XX:+UseCMSCompactAtFullCollection:此开关预设开启,用于消除碎块。每一次FULL GC都伴随一次碎块拆解-XX:CMSFullGCsBeforeCompaction=n:增设在继续执行无数次Full GC后对缓存内部空间展开填充重新整理。增设那个的前提是UseCMSCompactAtFullCollection开启-XX:+CMSInitiatingOccupancyFraction:增设 CMS 过滤器在老二十世纪内部空间被使用多少后促发,预设为 68%,增设得太高会触发Full gc影响性能。-XX:+CMSClassUnloadingEnabled:允许对类元数据展开拆解。也是允许CMS拆解方式区-XX:CMSInitatingPermOccupancyFraction=n:当永久性区占用率达到这一百分比后,开启 CMS 拆解 (前提是-XX:+CMSClassUnloadingEnabled激活了)。-XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才展开 CMS 拆解。初始标记和重新标记会Stop-The-World
CMS示例, 如图:我没有选择任何的实力派过滤器,只是开启了CMS
image.png
我已经圈出来了,能只要开启CMS就会自动选择ParNew
image.png
我将CMS的一部分拷贝下来
一共有7部分,对应着上面列出的CMS的7个步骤
G1
G1过滤器是JDK9预设的过滤器,jdk7和jdk8中都能用它,到了jdk9才正式成为预设,G1完全取代了CMS,针对CMS的碎块化展开了改进,使用G1不再须要与其他的过滤器配合,原因是在于G1对之前的缓存结构做了非常大的改动,不再把实力派和老二十世纪分别分配成一整块一整块的大区域,而是把之前的老二十世纪和实力派的Eden,survivior分割成一小块一小块的Region放在同一片堆区域,每块Region最大是32M,堆内部空间最多存放2048个Region,是最多是60G到70G,G1基于拷贝算法,高效的重新整理剩余缓存,而不须要管理缓存碎块,
G1有以下的特点:1、并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU来缩短stop-The-World停滞时间。部分其他过滤器原本须要停滞Java线程继续执行的GC动作,G1过滤器仍然能透过并发的方式让java程序继续继续执行。2、内部空间资源整合:G1过滤器从整体上看是采用标记重新整理算法,主要是把每一块当做两个标记单位,但是从局部也就每一块Region是基1除了追求低停滞外,还能建立可预测的停滞时间模型,能让使用者明确选定在两个长度为N毫秒时间片段内,耗用在垃圾收集上的时间不得超过N毫秒。4、分代收集:物理结构改变了,但是仍然保留着之前的分代概念,收集垃圾的基本原理依旧是分代收集G1的收集算法也比较独特,采用了分代并且分区的算法,分代是根据不同的块的特点,例如实力派的块就会使用拷贝算法,老二十世纪的块使用标记填充,并且每个块都分配两个remember set集合记录所属块和其他块的第一类引用关系,透过检查那个集合找出垃圾第一类,避免了扫描整座确定可达关系。
一般来说,内部空间越大扫描的范围就越大,停滞时间就长,但是展开分块能减少每天扫描整座堆内部空间的时间
缓存结构如图,其中Humongous是大第一类,大第一类直接进入Humongous的Region
同时还有两个并发标记过程记录的G区,G区是垃圾第一类比率较高的区域,混合拆解会优先清扫的区域
image.png
G1的GC模式两种:
1、Young GC:G1对实力派的 Region展开GC2、Mixed GC:根据并发标记过程统计得出收集收益高的Region展开GC。在用户选定的开销目标范围内尽可能选择收益高 Region,以此来控制Mixed GC的时间开销,达到可预测停滞,这里处理的Region就G区,老二十世纪和实力派的Region都可能成为G区G1的组织工作流程主要分两个阶段:并发标记和混合GC
1、并发标记:
初始化标记:那个过程会产生STW,标记可达第一类,同时展开一次young gc清扫eden区2、混合清扫:
并发标记完成后,G1会根据增设的停滞时间,优先选择清扫性价比高的区域,也是G区
G1的流程
full gc
和CMS一样,G1也是个并发的拆解器,当严重不足以提供足够多缓存给用户程序时同样会促发full gc
G1过滤器演示:
image.png
G1真的比较复杂,算法和机制都比较难,日志多了很多
image.png
GC篇总结:
停滞时间参数,如:G1的MaxGCPauseMillis,parallel的MaxGCPauseMillis假如调低了GC单次就会上升。线程参数,如:parallel的ParallelGCThreads,CMS的ParallelCMSThreads,G1的ConcGCThreads调高在垃圾生产多并且cup紧张的场景会耗用cpu性能堆内部空间参数:透过Xmx Xms调整堆的大小不一,堆越多gc单次自然就会减少,但是堆内部空间大反而会增长gc时间,能根据情况增设停滞时间去缩短每天gc时间、或者使survivor减小有效的减少拷贝内部空间减少拷贝操作让第一类尽块进入老二十世纪然后使用CMS展开并发处理、或者直接使用G1过滤器开启阈值:G1的InitiatingHeapOccupancyPercent,CMS的CMSInitiatingOccupancyFraction开启阈值过高会容易促发full gc,开启阈值过低会增加普通gc单次System.gc:假如代码中有频密使用 System.gc(),那么须要使用-XX:-DisableExplicitGC禁止full gc假如本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,