衡量垃圾收集器的三项重要指标:
三者共同构成了一个“不可能三角”,一款优秀的收集器通常最多可以同时达成其中的两项
描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条垃圾收集线程同时工作,通常默认此时用户程序处于等待状态
描述的是垃圾收集器与用户线程之间的关系;同一时间收集器线程与用户线程都在运行;由于垃圾收集线程也占用一部分系统资源,所以应用程序的处理吞吐量将收到一定程度的影响
初始标记(CMS initial mark):标记GC Roots能直接关联到的对象,速度很快,但需要STW 并发标记(CMS concurrent mark):从每一个被初始标记获得的对象开始遍历整个对象图,耗时长,但用户线程可以与垃圾收集线程并发运行 重新标记(CMS remark):修正并发标记过程中由于和用户线程并发而导致标记产生变动的那部分对象,这个阶段虽然耗时,但远比并发标记的耗时大大缩短,此阶段仍需要STW 并发清除(CMS concurrent sweep):清理删除掉已经死亡的对象,由于不需要移动对象(标记-清除法),所以可以和用户线程并发进行
虚拟机曾提供了i-CMS(增量式并发收集器),尽量减少垃圾收集线程的独占资源的时间,减少对用户程序的影响;但因为实践证明i-CMS效果一般,从JDK7开始,i-CMS就已经声明为废弃了
浮动垃圾区别于空间碎片,其来源于并发标记阶段和并发清理阶段用户线程和GC线程并发时程序运行自然产生的新的垃圾,由于错过了本次的标记过程只能留待下次处理,因此称为浮动垃圾。 浮动垃圾的存在使得CMS收集器不能等待老年代几乎完全填满时才进行收集,而必须预留一部分空间供GC线程与用户线程并发运行时使用 JDK提供参数-XX:CMSInitiatingOccupancyFraction参数来设置当老年代空间达到多少时触发收集,JDK6时CMS收集器的启动阈值已经默认提升至92%;但此参数不宜设置过高,过高时容易导致给浮动垃圾预留空间不足,出现“并发失败”,这时候虚拟机不得不启动后备预案:冻结用户线程,临时启用Serial Old收集器来重新进行老年代的垃圾收集,这将使得停顿时间更长
CMS收集器提供参数-XX:+UseCMSCompactAtFullCollection,用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程,同时,搭配-XX:CMSFullGCsBeforeCompaction参数,此参数要求CMS收集器在执行过若干次不整理空间的Full GC之后,下一次进入Full GC前会先进行碎片整理(这两个参数在JDK9开始废弃)
基于Region的内存布局:G1仍是遵循分代收集理论设计的,但区别于其他垃圾收集器对堆内存划分,G1不再坚持固定大小以及固定数量的分代区域划分,而是将连续的Java堆划分为多个大小相等的独立区域,称为Region,新生代和老年代也不再固定,它们都是一系列区域的动态集合;每个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者老年代空间;通过参数-XX:G1HeapRegionSize设置每个Region的大小 Humongous区域:Region中专门用来存储大对象的区域。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象,G1的大多数行为都把Humongous Region作为老年代的一部分来进行看待 区域的优先级回收方式:使用Region作为单次回收的最小单元,避免了在整个Java堆上进行全区域的垃圾收集,让G1收集器跟踪各个Region里面的垃圾堆积的回收收益(回收所获的空间以及回收所需的时间)的大小,然后在后台维护一个优先级列表,每次根据用户设定的收集停顿大小(-XX:MaxGCPauseMillis),优先处理回收收益最大的Region,提高回收的效率 原始快照处理并发:在与用户线程并发时,必须保证其不能打破原本的对象图结构,CMS收集器采用了增量更新算法实现,G1则采用了原始快照(SATB)算法实现;G1为每个Region设计了两个名为TAMS的指针,将TAMS指针位置以上的空间用于并发回收过程中新对象的分配 并发失败:与CMS一样,如果内存回收速度赶不上内存分配速度,也会导致Concurrent Mode Failure从而导致一次Full GC
初始标记(Initial Marking):标记GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配对象,这个阶段需要STW,而且是借用Minor GC的时候同步完成的,耗时很短,没有额外的停顿 并发标记(Concurrent Marking):从GC Roots开始对堆中对象进行可达性分析,递归扫描整个对象图,耗时较长,但可与用户程序并发执行,当对象图扫描完成后,还要重新处理SATB记录下的在并发时有引用变动的对象 最终标记(Final Marking):对用户线程做另一个短暂的暂停,STW,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录 筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来执行回收计划,可以选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间,这里的操作涉及存活对象的移动,必须STW,由多条收集器线程并行完成
Shenandoah收集器、ZGC收集器
初始标记(Initial Marking):与G1一样,首先标记与GC Roots直接关联的对象,这个阶段需要STW,但停顿时间与堆大小无关,只与GC Roots的数量有关 并发标记(Concurrent Marking):与G1一样,遍历对象图,标记全部可达对象,这个过程与用户线程并发,时间长短取决于堆中存活对象的数量以及对象图的结构复杂程度 最终标记(Final Marking):与G1一样,处理剩余的SATB扫描,并在这个阶段统计出回收价值最高的Region,将这些Region组成回收集,这一阶段存在短暂的STW 并发清理(Concurrent Cleanup):这个阶段用于清理那些整个区域连一个存活对象都没有的Region 并发回收(Concurrent Evacuation):并发回收阶段是Shenandoah收集器与之前HotSpot中其他收集器的核心差异。在这个阶段,Shenandoah要把回收集里存活对象先复制一份到其他未使用的Region之中,Shenandoah通过读屏障和“Brooks Pointers”转发指针处理了这期间用户线程和GC线程并发存在的对象移动问题 初始引用更新(Initial Update Reference):建立一个线程集合点,确保所有并发回收阶段中进行的收集器线程都已完成分配给它们的对象移动任务,这一阶段需要短暂的STW 并发引用更新(Concurrent Update Reference):并发回收阶段复制对象结束且已完成了初始引用更新阶段的处理后,需要把堆中所有指向旧对象的引用修正到复制后的新地址,这个阶段与用户线程一起并发,时间长短取决于内存中涉及的引用数量,且搜索对象的方式不是按图搜索而是按照内存物理地址线性地搜索出引用类型,将旧值改为新值 最终引用更新(FInal Update Reference):解决了堆中的引用更新后,还要修正存在于GC Roots中的引用,这个阶段需要STW,停顿时间只与GC Roots数量相关 并发清理(Concurrent Cleanup):经过并发回收和引用更新之后,整个回收集中所有Region已再无存活对象,最后再调用一次并发清理过程来回收这些Region的内存空间,供以后新对象的分配
# JDK9之前
-XX:+PrintGC
# JDK9之后
-Xlog:gc
# JDK9之前
-XX:+PrintGCDetails
# JDK9之后
-Xlog:gc*
# JDK9之前
-XX:+PrintHeapAtGC
# JDK9之后
-Xlog:gc+heap=debug
# JDK9之前
-XX:+PrintGCApplicationConcurrentTime
或者
-XX:+PrintGCApplicationStoppedTime
# JDK9之后
-Xlog:safepoint
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机运行在Client模式下的默认值,使用Serial+Serial Old的收集器组合 |
UseParNewGC | 使用ParNew+Serial Old的收集器组合,JDK9后不再支持 | |
UseConcMarkSweepGC | 使用ParNew+CMS+Serial Old的收集器组合,Serial Old作为CMS并发失败后的后备方案 |
UseParallelGC | JDK9之前运行在Sever模式下的默认值,使用Parallel Scavenge+Serial Old的收集器组合 |
UseParallelOldGC | 使用Parallel Scavenge+Parallel Old的垃圾收集器组合 |
SurvivorRatio | 新生代Eden与Survivor区域容量比值,默认是8,即8:1 |
PretenureSizeThreshold | 直接晋升到老年代对象大小,大于这个参数大小的对象将直接在老年代分配,此参数只对Serial和ParNew两款新生代收集器有效 |
MaxTenuringThreshold | 晋升到老年代的年龄,每个对象坚持过一次Minor GC年龄就会+1 |
UseAdaptiveSizePolicy | 自动调整Java堆中各区域的大小以及进入老年代的年龄 |
UseG1GC | JDK9后Server的默认值 |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。