JVM全文
概述
- JVM规范对垃圾回收器没有特别要求,可以由不同厂商、不同版本的JVM来实现
分类
线程数
来分,串行垃圾收集器
和并行垃圾收集器
- 串行回收,在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束;默认被应用在客户端模式下的JVM
- 在单CPU或者较小的应用内存等硬件平台不优越的场合,性能表现超过
并发和并行
- 并行收集运用
多个CPU
同时执行垃圾回收,采用独占式,也会导致STW
,产生的停顿时间短于串行
工作模式
来分,并发式垃圾回收器
和独占式垃圾回收器
- 并发式,垃圾回收线程与用户线程交替执行,尽可能减少用户线程停顿时间,提高响应,减小延迟
- 独占式,一旦运行,就会
STW
,停止应用程序的用户线程,直到垃圾回收过程完全结束
碎片处理方式
来分,压缩式垃圾回收器
和非压缩式垃圾回收器
- 压缩式,回收完成后,对存活对象进行压缩整理,消除回收后的碎片;指针碰撞
- 非压缩式,不进行整理;空闲列表来维护,记录空闲的内存空间
工作内存区间
来分,年轻代垃圾回收器
和老年代垃圾回收器
性能指标
吞吐量
,运行用户代码的时间占总运行时间的比例,越大越好
- 总运行时间=程序运行时间+内存回收时间
- 100分钟,垃圾收集1分钟,吞吐量99%
- 高吞吐量的应用程序有更长的时间基准,不用去考虑暂停时间
- 垃圾收集开销,吞吐量的补数,垃圾收集所用的时间与总运行时间的比例,
越小越小
暂停时间
,STW,执行垃圾收集时,程序的工作线程被暂停的时间,越短越好
- 收集频率,相对于应用程序的执行,收集操作发生的频率,
- 频率低->暂停总时间长->某段时间内吞吐量低
- 频率高->暂停总时间短->某段时间内吞吐量高
内存占用
,Java堆区所占的内存大小,越大越好
- 快速,一个对象从诞生到被回收所经历的时间,
越快越好
- 吞吐量、暂停时间、内存占用,构成不可能三角,不能同时具备
- 抓住两点,吞吐量和暂停时间(最重要,追求低延迟)
- 内存空间大->收集频率低->某段时间吞吐量提高,但是只要进行GC,暂停时间提高,延迟提高,在包含GC的时间段中,吞吐量减少
- 吞吐量优先,单位时间内,STW总时间最短,单次STW时间长
让用户感觉只用用户线程在执行,用户线程连续执行的时间较长
- 暂停时间优先,注重低延迟,内存空间小,单次STW时间最短,但是回收频率高,单位时间的STW长,吞吐量下降
对于一个交互式的应用程序,具有较低的暂停时间很重要
高吞吐量
和低暂停时间
是相矛盾的
- 吞吐量优先,需要降低内存回收的频率,但是导致GC需要更长的暂停时间来执行回收
- 低延迟有限,频繁执行内存回收,引起年轻代内存的缩减,导致吞吐量下降
- 目前的标准,
G1垃圾回收器
,以最大吞吐量优先情况下,降低最大停顿时间
垃圾回收器发展
SerialGC
,大对象无法进入时,执行GC,先将Eden的对象放入老年代,将大对象放在新生代;ParallelGC
,大对象无法进入时,直接进入老年代- 第一款GC,串行
Serial GC
ParNew
是Serial
的多线程版本Parallel GC
在JDK6之后称为HotSpot默认GC- JDK9中G1变成默认垃圾收集器,替代
CMS,Concurrent Mark Sweep
,第一款并发垃圾回收器,达到低延迟 - JDK10中G1可以进行
并行完整垃圾回收
,实现并行性来改善最坏情况下的延迟 - JDK11,引入
Epsilon垃圾回收器
,称为No-Op(无操作)
;ZGC
,可伸缩的低延迟垃圾回收器(实验版本) - JDK12,增强G1,自动返回未用堆内存给操作系统;引入
Shenandoah GC
,低停顿时间的GC(实验班) - JDK13,增强ZGC,自动返回未用堆内存给操作系统
- JDK14,删除CMS垃圾收集器,扩展ZGC在mac和win上的应用,原先是在linux上测试
- 工作模式
- 串行 Serial、Serial Old
- 并行 ParNew、Parallel Scavenge(框架和CMS不一样,不能兼容)、Parallel Old
- 并发 CMS、G1
- 工作区域
- Young Gen: Serial GC、Parallel Scavenge GC、ParNew GC
- Old Gen: Serial Old GC、Parallel Old GC、CMS GC
- Young/Old Gen: G1 GC
- 组合关系
- Serial GC + Serial Old GC (MSC)
- ParNew GC + CMS GC(JKD14 被移除) + Serial Old GC (MSC 后备方案,如果CMS回收失败,就使用这个)
- Parallel Scavenge GC + Parallel Old GC(G1前使用的)
- 针对不同场景,提供不同的垃圾回收器,提高垃圾收集的性能
- 查看默认垃圾回收器
-XX:+PrintCommandLineFlags
//jdk8
-XX:InitialHeapSize=134217728
-XX:MaxHeapSize=2147483648
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseParallelGC //垃圾回收器 JDK8
//jdk16
-XX:ConcGCThreads=1
-XX:G1ConcRefinementThreads=4
-XX:GCDrainStackTargetSize=64
-XX:InitialHeapSize=134217728
-XX:MarkStackSize=4194304
-XX:MaxHeapSize=2147483648
-XX:MinHeapSize=6815736
-XX:+PrintCommandLineFlags
-XX:ReservedCodeCacheSize=251658240
-XX:+SegmentedCodeCache
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseG1GC
复制代码
- 控制台查看
- jinfo -flag UseParallelGC 38077
- jinfo -flag UseParallelOldGC 38077
- jinfo -flag UseG1GC 38224
Serial 回收器
- 串行回收,Client模式下默认新生代垃圾收集器
- 采用复制算法、串行回收和
STW
机制的方式执行内存回收 - Serial Old,针对老年代,使用
标记-压缩算法
,是Client模式下默认的老年代垃圾收集器 - Serial Old在Server模式下的用途
- 与新生代Parallel Scavenge配合使用,现在已经取消
- 作为老年代CMS收集器的后备垃圾收集方案
- 只使用一个CPU或一条收集线程完成垃圾收集工作,在收集工作时,暂停其他所有工作线程
- 对于单个CPU环境中,没有线程交互的开销,专心做垃圾收集获得最高线程收集效率
-XX:+UseSerialGC
指定使用串行收集器
ParNew 回收器
- Rarallel New,只能处理新生代,并行回收
- Serial收集器的多线程版本,基本算法没有区别
- 复制算法,STW机制
- Server模式下新生代的默认垃圾回收器
- 对于新生代,回收次数频繁,并行方式高校
- 对于老年代,回收次数少,串行节省资源,CPU并行需要切换线程,串行可以省去切换线程的资源
- ParNew在多CPU环境下,重复利用物理硬件资源优势,快速完成垃圾收集
- 单CPU,串行回收不需要进行频繁线程切换
-XX:+UseParNewGC
指定垃圾收集器,jdk9及以后弃用-XX:ParallelGCThreads=2
设置线程数量,默认开启和CPU数量相同
Parallel Scavenge 回收器
- 吞吐量优先,组合JDK8默认
- 复制算法、并行回收、STW
- 目标是达到一个可控制的吞吐量,拥有自适应调节策略,调节内存分配,达到最优策略
- 尽快完成程序的运算任务,适合后台运算不需要太多交互任务,常见服务器环境下
- Parallel Old,针对老年代,采用标记-压缩算法,并发回收、STW
- 参数设置
-XX:UseParallelGC
,互相激活,默认,跟老年代是一组搭配-XX:UseParallelOldGC
-XX:ParallelGCThreads
设置年轻代线程数量,默认开启和CPU数量相同;CPU数量小于8,默认为CPU数量;CPU数量大于8,值为3+[5*count]/8
-XX:MaxGCPauseMills
,最大停顿时间,STW的时间,自动调整java堆的大小和分配;暂停时间越长->比例越大,影响下面的参数;暂时时间越短->越频繁->总时间越长->吞吐量下降(某个时间段)-XX:GCTimeRatio
,垃圾回收时间占总时间的比例,=1 / (N + 1)
,默认值N=99;-XX:+UseAdaptiveSizePolicy
,自适应调节,年轻代大小,Eden和s1/s0的比例,晋升老年代的阈值自动调整
CMS回收器
- Concurrent Mark Sweep,并发性标记清除垃圾收集器,第一款真正意义上的并发收集器,实现了垃圾收集线程与用户线程同时工作(切换)
- 针对老年代
- 尽可能缩短用户线程停顿时间,低延迟,快速响应
- 标记清除算法、STW
- 初始标记,Initial-Mark,所有用户线程STW,
仅仅只是标记出GCRoots能直接关联到
的对象,需要标记的对象很少,一旦标记完成后就恢复用户线程,执行速度非常快,STW时间短- 并发标记,Concurrent-Mark,从GCRoots的直接关联对象开始
遍历整个对象图的过程
(可达性分析),耗时长,但不需要停顿用户线程,并发执行- 重新标记,Remark,为了修正标记期间,
因用户程序运行导致标记产生变动
的那一部分对象的标记记录,怀疑是垃圾,需要确定是否为垃圾,进行STW,STW时间比初始标记稍长- 并发清除,Concurrent-Sweep,清理清除掉标记阶段判断的已经死亡的对象,释放内存空间,
不需要移动存活对象
,并发执行
- 由于垃圾收集阶段用户线程没有中断,回收过程中,需要
确保用户线程有足够的内存可用
,CMS不能等到老年代被填满后才进行收集,当堆内存使用率达到某一阈值
,开始回收
CMS运行时预留的内存无法满足程序需要,出现
Concurrent Mode Failure
,虚拟机启用后备方案,临时启用Serial Old收集器进行老年代的垃圾收集
- 采用标记清除算法,产生内存碎片,再分配空间时无法采用指针碰撞,需要
维护一个空闲列表
CMS采用的是并发清理,要保证用户线程还能继续执行,运行的资源不受影响,不能随意移动堆中对象的地址
- 优点,并发收集、低延迟
- 缺点
- 产生内存碎片,导致并发清除后,用户线程可用的空间不足,在无法分配大对象的情况下,提前触发Full GC
- 对CPU资源资源非常敏感,在并发阶段,占用一部分线程导致应用程序变慢,总吞吐量降低
- 无法处理浮动垃圾,在并发标记中,用户线程执行产生的新的垃圾对象,CMS无法对这些垃圾对象进行标记,导致这些新垃圾没有被及时回收
- 参数
-XX:+UseConcMarkSweepGC
启用CMS,自动打开ParNew,采用ParNew+CMS+Serial Old-XX:CMSInitiatingOccupancyFraction=92
,堆内存使用阈值,默认=-1,降低Full GC执行次数;如果内存增长很快,设置较小的值,给用户线程留出足够空间,避免频繁触发老年代串行收集器;内存增长很慢,设一个较大的值,降低CMS出发频率,减少老年代回收可以较明显地改善应用程序性能-XX:+UseCMSCompactAtFullCollection
,执行完CMS后,进行压缩整理-XX:CMSFullGCsBeforeCompaction=0
,执行多少次CMS Full GC后,进行压缩整理-XX:ParallelCMSThreads
,设置CMS的线程数量,默认值为CPU个数,结果为(ParallelGCThreads+3)/4
小结
- 最小化使用内存和并行开销 Serial GC
- 最大化应用程序的吞吐量 Parallel GC
- 最小化GC的中断或停顿时间 CMS GC
G1 回收器
- 侧重点在于回收垃圾价值量最大的区间,垃圾优先,Garbage First
- 区域化分代式,在延迟可控的情况下尽可能提高吞吐量
- 用不同的Region来表示Eden、S0、S1、老年代等
- 有计划地避免在整个堆中进行全全区域的垃圾回收,跟踪各个Region里面的
垃圾堆积的价值大小(回收所获得的空间大小以及回收所需的时间)
,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region - 主要针对配备多核CPU及大容量内存的机器,极高概率满足GC停顿时间的同时,兼具高吞吐量的性能特征
-XX:UseG1GC
jdk8时还不是默认的,设置启动- 适用场景
- 面向服务端应用,针对大内存、多处理器的机器
- 需要低GC延迟,并具有大堆的应用
- G1表现比CMS好的场景,超过50%的Java堆被活动数据占用;对象分配频率或年代提升频率变化很大;GC停顿时间过长
- HotSpot垃圾收集器中,其他垃圾收集器使用内置专门的JVM垃圾回收线程执行GC的多线程操作,而G1可以
采用应用程序线程承担后台运行的GC工作
;当JVM的GC线程处理缓慢时,系统调用应用线程帮助加速垃圾回收处理
- 分区Region 化整为零
- 所有Region大小相同,在JVM生命周期不会改变,参数设置 1MB~32MB
- 新生代和老年代不再是物理隔离,逻辑上连续,都是Region的一部分集合所组成的
- 一个Region
只能是一个角色
,当回收完后,角色可以转换- Survivor区
不再强调S0/S1 From/To
,在处理上就会转换- Humongous,相当于
多个连续的Region和起来的区域
,用于存储超大对象
,如果超过1.5个region,就放到H;如果一个H区存不下,就寻找连续的H区进行存储;如果找不到,就需要启动Full GC;G1大多数时候会将H区作为老年代看待,回收的不频繁- 每一个region中存放的时候都是规整的,且通过指针碰撞来分配空间;region中也可以划分TLAB区域,线程私有堆空间,线程并行执行
优缺点
- 并行与并发
- 并行性,G1在回收期间,可以有多个GC线程同时工作,利用多核计算能力,进行STW
- 并发性,拥有与应有程序交替执行的能力,部分工作可以与应用程序同时执行
- 分代收集
- 区分年轻代和老年代,堆结构上,不要求区域是连续的,也
不坚持固定大小和固定数量
- 将堆空间分为若干个Region,包含了逻辑上的年轻代和老年代,每次回收后,Region的角色可以变换
- 同时可以回收年轻代和老年代
- 空间整合
- Region之间是复制算法
- 整体上看是标记-压缩算法
- 避免碎片化,有利于长时间运行
- Java堆非常大的时候,G1优势明显
- 可预测的停顿时间模型,软实时,soft real-time,尽可能在给定的时间内完成垃圾回收
- 建议可预测的停顿时间模型,明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
- G1可以只选取部分区域进行内存回收,缩小回收的范围,避免全局停顿
- 跟踪各个Region里面的垃圾堆积的价值大小,在有限时间内,获取尽可能高的收集效率
- 下限高
- 缺点
- 内存占用较高
- 额外的执行负载较高
- 小内存 CMS表现好;大内存,G1表现好
参数
-XX:+UseG1GC
,手动置顶-XX:G1HeapRegionSize=16m
设置每个Region的大小,值是2的幂,范围是1~32MB,目标根据最小的Java对大小,划分出2048个区域,堆空间的大小为2G~64G,默认是对内存的1/2000-XX:MaxGCPauseMillis
,设置期望达到的最大GC停顿时间指标,不一定能达到,默认200ms
值小->处理的region比较少->如果用户线程内存使用速率快->容易满->导致Full GC
-XX:ParallelGCThread
,设置STW时GC线程数的值,最多8-XX:ConcGCThreads
,设置并发标记的线程数,将n设置为并行垃圾回收线程数 占ParallelGCThread的1/4左右-XX:InitiatingHeapOccupancyPercent
,设置触发并发GC周期的Java堆占用率阈值,超过此值,触发GC,默认为45
使用步骤
- 确保开启
- 设置堆的最大内存
- 设置对大停顿时间
垃圾回收过程
- 用LinkedList空闲列表维护空闲的Region
Remembered Set
- 记忆集 Remembered Set,避免进行全局扫描
- 一个对象会被不同区域所引用,一个对象不可能是孤立的,会被任意Region中对象引用
- Reset的作用是记录当前Region中哪些对象被外部引用指向,比如Old区中的对象会指向Eden区的对象,当我们要回收某个Region的时候,直接遍历遍历当前Region中的所有对象就可以了,然后针对性的去找到那些指向当前对象的其他对象,最终发现当前对象是否是根可达的,如果不是,那就应该被删除
- 每一个Region都有一个RSet,每次引用类型数据写操作时(A中的引用指向了B),都会产生一个
Write Barrier
暂时中断操作- 检查将要
写入的引用指向的对象是否和该引用类型数据
是否在不同的Region,其他收集器检查老年代对象是否引用了新生代对象- 如果不同,通过
CardTable
把相关引用信息(在B中记录A的区域)记录到引用指向对象所在的Region对应的RSet中- 当进行垃圾收集时,在GCRoots的枚举范围加入RSet,避免全局扫描
- 案例举例,当进行MinorGC,通过GCRoots找到当前对象时,当前对象同时也被其他区域(年轻代的其他区域或者老年代区域)的对象引用,如果不进行全局扫描,从一般的GCRoots出发进行可达性分析,无法直接通过其他区域的对象到达当前对象,但是这个对象也不应该被删除;以前的垃圾回收器需要全局遍历,才能保证不被删除,但是G1,在引用数据写入时,就会将引用当前对象的其他对象的相关信息记录在RSet,在扫描当前Region时,GCRoots的范围就包括了这个RSet中的信息
- 案例二,当GCRoots可以到达A区域的某个对象,但是这个对象同时被B区域的某个对象引用,无法直接从GCRoots到达B区域的某个对象,通过RSets可以将B区域的对象加入A区域对象的对象图中,不被删除
年轻代
- 年轻代GC,Young GC
- Eden用尽时触发,并行的独占式的收集器,G1暂停所有应用程序线程,启动多线程执行,STW的
- 将Eden存活对象,移动到Survivor区,或者老年代;Survivor进行清理并复制到空闲区域,如果达到阈值,移动到老年代
- 采用复制算法
- 过程
- 扫描根/GCRoots,static变量指向的对象、正在执行的方法调用链条上的局部变量,Rset记录的外部引用作为扫描存活对象入口
- 更新RSet,处理dirty card queue(记录引用对象的操作,对象引用关系变化,先记录在队列中,保证性能,STW的时候进行更新)中的card,进行更新,保证RSet可以准确反映老年代对所在内存分段中对象的引用
- 处理RSet,识别被引用的对象,标记为存活
- 复制对象,复制算法,遍历对象树,Eden->Survivor中空的内存片段;Survivor存活对象未达阈值->年龄+1->到空闲区域;达到阈值->Old区;如果Survivor空间不够,Eden空间中的部分数据直接晋升到老年代
- 处理引用,软/弱/虚/终结器/JNI-Weak引用,最终Eden空间为空,GC停止工作,通过复制算法,新形成的Region对象是连续的,没有碎片;空闲区域记录在LinkedList列表中,
老年代并发标记过程
- 老年代并发标记过程,Concurrent Makring,包括YGC
- 过程
- 初始标记,标记从根节点直接可达的对象,
是STW的
- 根区域扫描,Root Region Scanning,G1扫描Survivor区可直接可达的老年代区域对象,并标记被引用的对象,这一过程必须在YGC之前完成,如果发生YGC,会处理年轻代
- 并发标记,Concurrent Marking,在整个堆中进行并发标记,可能被YGC中断;若发现区域中的所有对象都是垃圾,就会立即回收(实时回收),同时计算每个区域的对象活性(区域中存活对象的比例)
- 再次标记,Remark,修正上一次的标记结果,
STW
,采用比CMS更快的初始快照算法,snapshot-at-the-begining
,解决并发标记的对象消失问题- 独占清理,计算各个区域的存活对象和GC回收比例,并进行排序,识别可以混合回收的区域,
STW
,不会去做实际的垃圾收集- 并发清理阶段,识别并清理完全空闲的区域(百分之百要被回收的区域),将空闲的区域加入空闲列表
混合回收
- 混合回收 Mixed GC,包括YoungGC和OldGC
- 标记完成后,每个Region中的垃圾的内存分段就被计算出来,进行混合回收过程
- 这个过程中老年代和新生代是一起被回收的
- 整理,G1从老年代移动存活对象到空闲空间,这些空闲区间成为老年代一部分
- 不需要整个老年代被回收,一次只需要回收老年代中的部分Region
- 过程
- 老年代的内存分段会分8次进行回收
-XX:G1MixedGCCountTarget
设置- Collection Set混合回收的回收集,包括八分之一的老年代内存片段(老年代的总Region1/8部分),Eden区内存分段,Survivor区内存分段,采用复制算法
- G1优先考虑回收垃圾多的内存分段,并且有一个阈值决定内存分段(单个Region)是否被回收
-XX:G1MixedGCLiveThresholdPercent
,默认为65%,指的是垃圾占内存分段比例高达65%才会被回收;垃圾太少,存活对象过多,导致复制的时候花费更多的时间- 混合回收不一定要进行8次,
-XX:G1HeapWastePercent
,默认值为10%,允许整个堆内存有10%的空间被浪费,如果回收的垃圾占堆内存的比例低于10%(并发标记过程能记录垃圾总和),则不再进行混合回收,回收比很低
Full GC
- FUll GC,单线程、独占式、高强度
针对GC的评估失败,提供了一种保护机制,强力回收
- 导致的原因
- 复制算法转移对象时,没有足够的空间来存放,堆内存太小,复制对象没有空的内存分段可用Region,增大内存
- 并发处理过程完成前,空间耗尽
- 最大GC停顿时间太短,导致规定的时间间隔内无法完成垃圾回收,加大GC停顿时间
优化
- 回收阶段与用户线程并发执行
- 面向低延迟,停顿用户线程能够达到最大的幅度提高垃圾收集效率
- 年轻代大小
- 避免使用
-Xmn
或-XX:NewRatio
设置年轻代大小- 固定年轻代的大小会覆盖暂停时间目标
- 暂停时间不要太小
- G1吞吐量目标是90%的应用程序时间和10%的垃圾回收时间
- 评估G1吞吐量时,暂停时间太小表示承受更多的垃圾回收开销,影响到吞吐量
垃圾回收器总结
- 发展阶段 Serial->Parallel 并行->CMS 并发->G1->ZGC
- 如何选择垃圾回收器
- 让JVM自适应完成调整堆大小
- 内存小于100M,串行
- 单核、单机程序,没有停顿时间要求,串行
- 多CPU,高吞吐量、允许停顿时间超过1秒,并行或者JVM自行选择
- 多CPU,第停顿时间,快速响应,并发
日志分析
SerialGC
,大对象无法进入时,执行GC,先将Eden的对象放入老年代,将大对象放在新生代;ParallelGC
,大对象无法进入时,直接进入老年代- 参数,对于G1,345参数都不能使用
-XX:+PrintGC
,输出GC日志,类似-verbose:gc
-XX:+PrintGCDetails
,输出GC的详细日志-XX:+PrintGCTimeStamps
,输出GC的时间戳(以基准时间的形式,JVM启动以来到现在的时间)0.389-XX:+PrintGCDateStamps
,输出GC的时间戳(以日期的形式)2021-10-15T19:07:13.682+0800-XX:+PrintHeapAtGC
,进行GC的前后打印出堆的信息-Xloggc:/Users/apple/Desktop/java/jvm/logs/gc.log
日志文件的输出
- JDK16参数改变
-Xlog:gc*
-Xlog:gc:details
-Xlog:gc+heap=trace
-Xlog:gc:/Users/apple/Desktop/java/jvm/logs/gc.log
- PrintGC JDK8
//GC-新生代 Allocation Failure-新对象分配失败导致的GC
//16296K->14970K-前后变化 59392K-当前堆大小
[GC (Allocation Failure) 16296K->14970K(59392K), 0.0085846 secs]
[GC (Allocation Failure) 31292K->31288K(59392K), 0.0084852 secs]
[Full GC (Ergonomics) 31288K->31188K(59392K), 0.0090026 secs]
[Full GC (Ergonomics) 47541K->47182K(59392K), 0.0074301 secs]
复制代码
- PrintGCDetails
//PSYoungGen-Parallel收集器 ParOldGen-ParallelOld收集器
//user-垃圾收集器花费的总CPU时间
//sys-花费在等待系统调用或系统事件的时间
//real-GC从开始到结束的时间,包括其他进程占用时间片的实际时间
//16296K->2024K(18432K) 回收之前的大小 回收后的大小 总大小
[GC (Allocation Failure) [PSYoungGen: 16296K->2024K(18432K)] 16296K->15034K(59392K), 0.0098301 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 18346K->2008K(18432K)] 31356K->31328K(59392K), 0.0105358 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 2008K->0K(18432K)] [ParOldGen: 29320K->31188K(40960K)] 31328K->31188K(59392K), [Metaspace: 3241K->3241K(1056768K)], 0.0165703 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]
[Full GC (Ergonomics) [PSYoungGen: 16352K->6601K(18432K)] [ParOldGen: 31188K->40581K(40960K)] 47541K->47182K(59392K), [Metaspace: 3241K->3241K(1056768K)], 0.0193033 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
复制代码
- -XX:+PrintHeapAtGC
//GC堆的名字 total 总容量 used 已分配空间 [申请的虚拟空间下限,已分配的虚拟空间上限,申请的虚拟空间上限) 左闭右开
{Heap before GC invocations=1 (full 0):
PSYoungGen total 18432K, used 16296K [0x00000007bec00000, 0x00000007c0000000, 0x00000007c0000000)
eden space 16384K, 99% used [0x00000007bec00000,0x00000007bfbea280,0x00000007bfc00000)
from space 2048K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007c0000000)
to space 2048K, 0% used [0x00000007bfc00000,0x00000007bfc00000,0x00000007bfe00000)
ParOldGen total 40960K, used 0K [0x00000007bc400000, 0x00000007bec00000, 0x00000007bec00000)
object space 40960K, 0% used [0x00000007bc400000,0x00000007bc400000,0x00000007bec00000)
Metaspace used 3239K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 355K, capacity 388K, committed 512K, reserved 1048576K
复制代码
- G1信息,Region变化,-Xlog:gc*
[0.170s][info][gc,start ] GC(0) Pause Young (Normal) (G1 Evacuation Pause) //STW
[0.170s][info][gc,task ] GC(0) Using 2 workers of 4 for evacuation //执行的CPU数
[0.178s][info][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms //回收集
[0.178s][info][gc,phases ] GC(0) Merge Heap Roots: 0.0ms //RSet
[0.178s][info][gc,phases ] GC(0) Evacuate Collection Set: 8.1ms //执行回收时间
[0.178s][info][gc,phases ] GC(0) Post Evacuate Collection Set: 0.1ms //发布执行回收时间
[0.178s][info][gc,phases ] GC(0) Other: 0.2ms
[0.178s][info][gc,heap ] GC(0) Eden regions: 13->0(7) //region变化,括号里的是将7个再利用的意思吗?
[0.178s][info][gc,heap ] GC(0) Survivor regions: 0->2(2)
[0.178s][info][gc,heap ] GC(0) Old regions: 0->10
[0.178s][info][gc,heap ] GC(0) Archive regions: 2->2
[0.178s][info][gc,heap ] GC(0) Humongous regions: 0->0
[0.178s][info][gc,metaspace] GC(0) Metaspace: 629K(832K)->629K(832K) NonClass: 591K(704K)->591K(704K) Class: 37K(128K)->37K(128K) //元空间变化
[0.178s][info][gc ] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 13M->12M(60M) 8.586ms //结果
[0.178s][info][gc,cpu ] GC(0) User=0.01s Sys=0.01s Real=0.01s //事件信息
复制代码
日志工具
垃圾回收器新发展
Epsilon No-Op
,只做内存分配,不做垃圾回收,运行完之后直接退出程序Shenandoah GC
,低停顿时间
- Open JDK中的,不是Oracle公司主导开发的
- 低停顿的需求,暂停时间与对大小无关,限制在10ms之内
- 越频繁,总时间长,吞吐量下降
ZGC
低延迟垃圾回收器,可伸缩低延迟
- 尽可能对吞吐量影响不大的情况下,实现任意堆内存大小都可以将停顿时间控制到10ms以内
- 基于Region内存布局,不设分代,使用了
读屏障
、染色指针
和内存多重映射
等技术实现可并发的标记-压缩算法的,以低延迟为首要目标的垃圾收集器- 并发标记->并发预备重分配->并发重映射
- 几乎所有地方都是并发执行,
初始标记是STW的
,停顿时间几乎都耗费在初始标记上-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
使用
AloiGC
,阿里,基于G1算法,面向大堆Zing
,低延迟GC