赞
踩
目录
2.4. Generational Collection(分代收集)算法
3.2 Serial/Serial Old 串行收集器(-XX:+UseSerialGC)
3.3并行收集器( -XX:+UseParallelGC、-XX:+UseParallelOldGC)
3.4 并发收集器/CMS收集器 Concurrent Mark Sweep
GC (Garbage Collection:即垃圾回收):将内存中不再被使用的对象(非存活对象)进行回收,GC中用于回收的方法称为收集器。
当垃圾收集器在对堆进行回收前,第一就是要确定对象哪些是还在被引用的或者后面还需要被引用的,即存活,哪些是已经“死去”(即不可能再被任何途径使用)。回收的就是这些非存活的对象。
引用计数是垃圾收集器中的早期策略。堆中每个对象实例都有一个引用计数,当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。每当有一个地方引用它时,计数器值就加1,引用失效时就减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。
缺点:
很难解决对象之间相互循环引用的问题,就如果两个对象之间相互调用,这个地方相互调用会使得引用计数法始终认为有对象在引用当前对象,就一直计数值大于或等于1,也就无法通知GC收集器回收它们。但是实际的情况是这两个对象后面已经不再调用。
可达性算法是目前主流的虚拟机都采用的算法,程序把所有的引用关系看作一张图,从一个节点GC Roots开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。
1.Mark-Sweep(标记-清除)算法
2.Copying(复制)算法
3.Mark-Compact(标记-整理)算法(压缩法)
4.Generational Collection(分代收集)算法
https://www.html.cn/qa/other/22924.html
此算法执行分两阶段。
第一阶段 标记:遍历内存区域,对需要回收的对象打上标记。
第二阶段 清除:再次遍历内存,对已经标记过的内存进行回收。
此算法需要暂停整个应用
绿色、蓝色---代表存活对象
灰色---非存活对象
白色--未使用的内存
缺点:
该算法最大的问题是内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题。
效率问题:遍历了两次内存空间(第一次标记,第二次清除)。
空间问题:gc之后,会产生大量的内存碎片,随着gc次数增多,未使用的内存会被切割的越来越小。这些零碎的内存,足够小时,就不能存放连续的内存(如数组,数组是连续的内存空间),如数组10m,但是这一块内存只有1m,虽然这块内存未使用,但是也不能用来存放这个10m的数组。这种零碎的内存越来越多,则整体可以使用的内存越来越少。当再需要一块比较大的内存时,无法找到一块满足要求的,因而不得不再次出发GC。
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。
垃圾回收时,遍历当前使用区域, 把正在使用中的对象复制到另外一个区域中。
优点:此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。
缺点:最大的缺点就是需要两倍内存空间。且存活对象增多的话,Copying算法的效率会大大降低。
jvm执行YGC时,清理伊甸园区和两个存活区就使用的复制算法。
老年代默认使用的是 标记-清除 算法,不可以使用复制算法,使用标记-清除算法,内存都可以被回收掉,但是不能清除碎片内存。
在这两个算法之上,又出现了 标记-整理(Mark-Compact)算法,结合了两种算法的优点。
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,
第一阶段标记所有的存活对象
第二阶段遍历整个堆,清除非存活的对象并且把存活对象“压缩”到堆的其中一 块,按顺序排放。
此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
FGC,清理老年代时,期望使用标记-整理算法,
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。
这个算法并没有新的内容,只是根据对象的存活的时间的长短,将内存分为了新生代(Young Generation)和老年代(Tenured Generation),这样就可以针对不同的区域,采取对应的算法。
新生代(年轻代):
新生代的特点是每次垃圾回收时都有大量的对象需要被回收,大部分垃圾收集器对于新生代都采取复制算法,
因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
老年代:
老年代的特点是每次垃圾收集时只有少量对象需要被回收。对象存活时间长,采用标记-整理(压缩算法),或者标记清理算法都可。
JVM主要组成部分:
堆内存划分:
堆内(放的是对象,数组)等临时数据,用完就没啦
年轻代/新生代:eden +s1 +s0(eden:s1 :s0默认比例:8:1:1 )
老年代:(年轻代:老年代默认比例:1:2)
s0与s1大小相等,位置互换
堆外(放的是方法,类,常量)
所有新new的对象和数组,是在伊甸园区产生的,内存分配是在伊甸园区进行的。
当伊甸园区内存被占满时,就不能再new对象,程序依赖对象,所以程序也就不能运行。java应用程序为了能继续运行,就要干掉那些不被引用的对象,对这些不被引用的对象进行垃圾回收。
当伊甸园区被占满时,触发GC(垃圾回收 Garbage Collection)。
GC分两步,第1步标记,对非存活对象进行标记。第2步清扫,将非存活对象从内存中干掉,释放内存,同时将存活的对象移到存活区中。
YGC
发生在年轻代。
在YGC之前,伊甸园区是满的,无法new对象,程序无法运行。
YGC之后,伊甸园区就空了,就可以继续new对象。
第一次gc,,对存活的对象进行标记,然后将Eden区的存活对象,存活区S0
第二次gc,使用寻根判断的算法,对存活的对象进行标记,然后将Eden区的存活对象,移到存活区S0
步骤1:new的对象、数组,直接进入eden园区。
步骤2:eden园区满了,触发ygc,对【eden园区】的对象和数组进行标记(使用寻根判断的算法),清扫(将没有引用的对象干掉,被引用的移到其中一个存活区s0);
eden园区第二次满了,再次触发ygc,对【eden园区和存活区s0】的对象和数组进行标记,清扫将没有引用的干掉,被引用的对象和数组移到在另一个存活区s1(这里用的是复制算法)
步骤3:fgc,全gc,堆内存和持久代均gc。将没有的干掉,有用的放到老年代。
年轻代里面的两个存活区,大小相等,且同一时间只有一个存活区有对象,另一个存活区为空。gc之前,存活区s0有对象,s1为空,一次gc之后,s0为空,s1存放对象。
对象进入老年代原则:
1、大对象直接进入老年代
在堆中分配的大对象直接挪到老年代
大对象是指需要大量连续内存空间的对象,例如很长的字符串以及数组。
虚拟机设置了一个-XX:PretenureSizeThreshold参数,有个默认值,令大于这个设置的对象直接在老年代分配,这个值是可以修改的。
目的就是为了防止大对象在Eden空间和Survivor空间来回大量复制,大对象很容易把伊甸园区占满,导致YGC频繁。
2、长期存活的对象进入老年代(伴随YGC产生的)
虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden区出生并经过第一次YGC/Mintor GC后仍然存活,并且能被Survivor接纳,并被移动到Survivor空间上,那么该对象年龄将被设置为1。
对象在Survivor区中每熬过一次YGC/Minor GC,年龄就加一,当他的年龄增加到一定程度,就会被移动到老年代(年龄值默认为15)。
对象晋升老年代的阈值可以通过-XX:MaxTenuringThreshold设置。
3、动态年龄判断并进入老年代(伴随YGC产生的)
为了更好的适应不同程序的内存状况,虚拟机并不是永远要求对象的年龄必须达到MaxTenuringThreshold才会晋升到老年代。
如果在Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到MaxTensuringThreshold的要求年龄。
(相同age对象大小之和>1/2存活区(即s0或者s1)则所有>=age的对象就会进入老年代)
举个栗子:
比如:MaxTenuringThreshold 为15,Survivor内存大小为100M。age 为1的,所有对象大小之和为10M。age 为5的,所有对象大小之和为51M。age为6的,所有对象大小之和为5M.
因为age 为5的对象所占内存之和已经超过了Survivor空间的一半,所以age为5,和age大于5的对象,都要移到老年代(没有age达到15的限制)
4、一次Young GC时数据放到存活区,但是存活区满了导致放不下去此时直接进入老年代
尽可能避免这种情况,如果对象直接从Eden区到老年代,那么存活区就没有什么存在的意义了,之所以设置存活区,就是为了将没有引用的对象更早的回收掉,将内存腾出来。
当老年代满了之后,触发Full GC。
Full GC 范围:
整个堆内存(年轻代+老年代)+园数据区
对堆的回收:
标记--清扫
对堆标记,对象是否被引,被引用的为存活对象。
对园数据回收:
标记--清扫
标记类,哪些类失效了。
回收,将失效的类卸载掉。
FGC时长,大小跟内存大小有关系,内存大,则FGC时间长。
时长不太好控制,但是我们可以控制FGC频次。
当老年代和年轻代,被存活的对象占满时(GC也不能将这些存活的对象清理掉),在Eden就不能再创建新的对象,导致OOM。
gc的触发条件
触发条件:
YGC:
有且只有这一种情况,eden满了,触发gc
FGC:
1、老年代满了
2、园区某些类已经失效了,在加载的找不到这个类,也会触发FGC,去卸载这个类,释放这个类。
3、空间担保原则(主要触发fgc的方式)
4、代码里面显示调用
5、jmap dump
内存担保机制
现代虚拟机把新生代分为三个区域,一个Eden区域,两个Survivor区域,Eden区域与Survivor区域的比例大小是8:1,虚拟机在YGC/Minor GC时在新生代采用复制算法,将存活对象复制到一个Survivor上面,如果Survivor空间不够用时,就需要老年代进行分配担保。
在发生Minor GC之前虚拟机会先检查老年代最大可用的连续空间是否大于新生代对象的总空间。如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,虚拟机会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代的平均年龄(因为事先不知道存活对象的内存空间,所以取了平均值)。若果大于,虚拟机会尝试进行一次Minor GC,但是这次Minor GC存在风险。如果小于,或者HandlePromotionFailure不允许担保,那这次也要改为Full GC
空间担保原则:
每次ygc的时候,都会往老年代里放对象。
根据历史每次往老年代放的对象大小,根据一个算法,估算这一次要放对象大小。估算的数值,小于老年代剩余内存,就执行ygc。
如果大于老年代剩余空间,放弃本次ygc,直接fgc。
代码里面显示调用
代码里写了system.gc或者get run time .gc
fgc特别消耗cpu,尽可能把FGC频次减少,最起码要一小时以上
垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现,用户可以根据自己的需求组合出各个收集器。
jdk8环境下,默认使用 Parallel Scavenge收集器(新生代)+ Serial Old收集器(老年代)
新生代使用Parallel Scavenge收集器(并行收集器):
使用的算法是基于标记-复制算法实现。收集器的目标是达到一个可控制的吞吐量,如何计算:吞吐量=用户代码运行时间/(代码运行时间+垃圾收集时间),重点关注一个参数吧 -XX:UserAdaptiveSizePolicy 这个参数激活后,不需要人工的指定新生代大小(-Xmn)、Eden与Surivivor区的比例,晋升老年代对象大小等参数了,虚拟机会根据当前系统运行情况收集性能监控信息动态调整这些参数以提供最合适的停顿时间合或最大的吞吐量。
老年带使用的是Parallel Old收集器(并行收集器):
是Parallel Scavenge的老年代版本,基于标记-整理算法实现,支持多线程并行收集。他的出现缓解了Parallel Scavenge的尴尬处境,因为Parallel Scavenge和别的优秀的老年代收集器不搭。出现后他俩搭配,才让吞吐量优先的收集器名副其实。
1.Serial/Serial Old串行收集器(-XX:+UseSerialGC)
是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。不适合多线程项目,效率低下。
2.ParNew收集器(-XX:UserParNewGC)
是Serial(串行)收集器的多线程版本,使用多个线程进行垃圾收集。如果服务器是单核CPU,那么效率低单线程(Serial)串行回收器。
3.Parallel Scavenge收集器 (并行收集器) -XX:+UseParallelGC
是一个新生代的多线程收集器,它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
4.Parallel Old收集器(并行收集器)-XX:+UseParallelOldGC
是Parallel Scavenge收集器的老年代版本,使用多线程和Mark-Compact(标记-整理)算法。
5.CMS(Concurrent Mark Sweep)收集器(并发收集器)-XX:+UseConcMarkSweepGC)
是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep(标记-清除)算法。如果响应时间重要性大于吞吐量,并且要求服务器响应速度快,建议使用CMS。该回收器是多线程并发,适合多核机器,在GC时,会占用较高的CPU资源。
6.G1-GarbageFirst收集器(-XX:+UseG1GC)
是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。
但是,也无法使用多处理 器的优势,所以此收集器适合单处理器机器。
当然,此收集器也可以用在小数据量(100M 左右)情况下的多处理器机器上。
可以使用-XX:+UseSerialGC 打开。
年轻代YGC
串行垃圾回收,如图一共4个cpu,YGC用一个线程来进行垃圾回收(寻根判断--复制算法),
YGC时只有一个线程进行垃圾收集,此时应用程序处于暂停状态,不能工作。
老年代FGC
串行收集,标记-整理算法,一个线程进行垃圾收集,只有一个线程干活,效率比较低。
串行收集这种效率比较低,基本上已经废弃了。如果没有单独配置垃圾回收策略,1.7及之前的JDK,默认用的就是这种垃圾回收器。
1.8JDK如果不配置垃圾回收器,默认是并行收集器。
年轻代,执行YGC时,多个线程进行寻根判断,标记存活对象,多个线程进行通过复制算法进行垃圾回收,可以发挥多核cpu的优势,效率比较高。
但是老年代依然使用的串行收集,所以老年代还是比较慢。
对年轻代迕行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用 -XX:+UseParallelGC.打开。
并行收集器在 J2SE5.0 第六 6 更新上引入,在 Java SE6.0 中迕行了增强 --可以对年老代迕行并行收集。
如果年老代不使用并发收集的话,默认是使用单线程进行垃圾回收, 因此会制约扩展能力。使用-XX:+UseParallelOldGC 打开。
所以:
如果只配置 -XX:+UseParallelGC,只是单纯的并行年轻代。
打开-XX:+UseParallelOldGC,就可以同时并行老年代了
使用-XX:ParallelGCThreads=设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相 等。
并行收集器,年轻代并行收集、老年代并行收集时,都会暂停应用程序,如果内存足够大,暂停时间依然很长。
并发收集器简称CMS收集器(Concurrent Mark Sweep),
CMS主要减少年老代的暂停时间,可以保证大部分工作都并发进行(应用不停止),垃圾回收只暂停很少的时间,此收集器适合对响应 时间要求比较高的中、大规模应用。
使用-XX:+UseConcMarkSweepGC 打开。
其中“Concurrent”并发是指垃圾收集的线程和用户执行的线程是可以同时执行的。
年轻代使用复制算法,老年代使用标记-清除(不是标记-整理)。
CMS是基于“标记-清除”算法实现的,整个过程分为4个步骤:
1、初始标记(CMS initial mark)。
2、并发标记(CMS concurrent mark)。
3、重新标记(CMS remark)。
4、并发清除(CMS concurrent sweep)。
注意:“标记”是指将存活的对象和要回收的对象都给标记出来,而“清除”是指清除掉将要回收的对象。
初始标记、重新标记这两个步骤仍然需要暂停应用。
初始标记:初始标记只是标记一下GC Roots能直接关联到的对象,速度很快。单线程标记,暂停应用程序,不使用多线程原因,
1、初始标记这个过长极短
2、如果是多线程标记,最后还要同步数据,消耗时间可能会更长
并发标记:并发标记阶段(这个阶段不会阻碍业务线程继续执行)就是进行GC Roots Tracing(从GC Roots开始找到它能引用的所有其它对象)的过程。
重新标记:重新标记阶段则是为了修正并发标记期间因用户程序继续动作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
CMS收集器的动作步骤如上图所示,在整个过程中耗时最长的并发标记和并发清除过程,收集器线程都可以与用户线程一起工作,因此,从总体上看,CMS收集器的内存回收过程是与用户线程一起并发执行的。
优点:并发收集、低停顿【注意:这里的停顿指的是停止用户线程】,Oracle公司的一些官方文档中也称之为并发低停顿收集器(Concurrent Low Pause Collector)。
年轻代-多个线程进行垃圾回收工作,暂停应用程序,但是暂停时间会比较短。
在每个老年代垃圾回收周期中,在收集初期并发收集器 会对整个应用进行简短的暂停,在 收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。
原文链接:https://blog.csdn.net/coderlius/article/details/79272773
概述
G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一,从设计目标看 G1 完全是为了大型应用而准备的。 同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。
G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。
支持很大的堆
高吞吐量
--支持多 CPU 和垃圾回收线程
--在主线程暂停的情冴下,使用并行收集
--在主线程运行的情冴下,使用并发收集
实时目标:可配置在 N 毫秒内最多只占用 M 毫秒的时间进行垃圾回收
当然 G1 要达到实时性的要求,相对传统的分代回收算法,在性能上会有一些损失。
开启选项:-XX:+UseG1GC
之前介绍的几组垃圾收集器组合,都有几个共同点:
1.年轻代、老年代是独立且连续的内存块;
2.年轻代收集使用单eden、双survivor进行复制算法;
3.老年代收集必须扫描整个老年代区域;
4.都是以尽可能少而快地执行GC为设计原则。
G1垃圾收集器也是以关注延迟为目标、服务器端应用的垃圾收集器,被HotSpot团队寄予取代CMS的使命,也是一个非常具有调优潜力的垃圾收集器。
虽然G1也有类似CMS的收集动作:初始标记、并发标记、重新标记、清除、转移回收,并且也以一个串行收集器做担保机制,但单纯地以类似前三种的过程描述显得并不是很妥当。
事实上,G1收集与以上三组收集器有很大不同:
1、G1的设计原则是"首先收集尽可能多的垃圾(Garbage First)"。因此,G1并不会等内存耗尽(串行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;
2、G1采用内存分区(Region)的思路,将内存划分为一个个相等大小的内存分区,回收时则以分区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位进行操作,因此G1天然就是一种压缩方案(局部压缩);
3、G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换;
4、G1的收集都是STW(STW: Stop The World)的,但年轻代和老年代的收集界限比较模糊,采用了混合(mixed)收集的方式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。
G1 可谓博采众家之长,力求到达一种完美。他吸取了增量收集优点,把整个堆划分为一个一个等大 小的区域(region)。
内存的回收和划分都以 region 为单位;同时,他也吸取了 CMS 的特点,把 这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;而且,G1 也认同分代垃圾回收的思想, 认为不同对象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。
为了达到 对回收时间的可预计性,G1 在扫描了 region 以后,对其中的活跃对象的大小进行排序,首先会收集 那些活跃对象小的 region,以便快速回收空间(要复制的活跃对象少了),因为活跃对象小,里面 可以认为多数都是垃圾,所以这种方式被称为 Garbage First(G1)的垃圾回收算法,即:垃圾优先 的回收。
初始标记(Initial Marking)
G1 对于每个 region 都保存了两个标识用的 bitmap,一个为 previous marking bitmap,一个为 next marking bitmap,bitmap 中包含了一个 bit 的地址信息来指向对象的起始点。
开始 Initial Marking 之前,首先并发的清空 next marking bitmap,然后停止所有应用线程,并扫 描标识出每个region中root可直接访问到的对象,将region中top的值放入next top at mark start (TAMS)中,之后恢复所有应用线程。
触发这个步骤执行的条件为:
G1定义了一个JVM Heap大小的百分比的阀值,称为h,另外还有一个 H,H的值为(1-h)*Heap Size, 目前这个 h 的值是固定的,后续 G1 也许会将其改为动态的,根据 jvm 的运行情况来动态的调整,在 分代方式下,G1 还定义了一个 u 以及 soft limit,soft limit 的值为 H-u*Heap Size,当 Heap 中使 用的内存超过了 soft limit 值时,就会在一次 clean up 执行完毕后在应用允许的 GC 暂停时间范围内 尽快的执行此步骤;
在 pure 方式下,G1 将 marking 与 clean up 组成一个环,以便 clean up 能充分的使用 marking 的信息,当 clean up 开始回收时,首先回收能够带来最多内存空间的 regions,当经过多次的 clean up,回收到没多少空间的 regions 时,G1 重新初始化一个新的 marking 与 clean up 构成的环。
并发标记(Concurrent Marking)
按照之前 Initial Marking 扫描到的对象进行遍历,以识别这些对象的下层对象的活跃状态,对于在 此期间应用线程并发修改的对象的以来关系则记录到 remembered set logs 中,新创建的对象则放 入比 top 值更高的地址区间中,这些新创建的对象默认状态即为活跃的,同时修改 top 值。
最终标记暂停(Final Marking Pause)
当应用线程的 remembered set logs 未满时,是不会放入 filled RS buffers 中的,在这样的情况下, 这些 remebered set logs 中记录的 card 的修改就会被更新了,因此需要这一步,这一步要做的就 是把应用线程中存在的 remembered set logs 的内容进行处理,并相应的修改 remembered sets, 这一步需要暂停应用,并行的运行。
存活对象计算及清除(Live Data Counting and Cleanup)
值得注意的是,在 G1 中,并不是说 Final Marking Pause 执行完了,就肯定执行 Cleanup 这步的, 由于这步需要暂停应用,G1 为了能够达到准实时的要求,需要根据用户指定的最大的 GC 造成的暂 停时间来合理的规划什么时候执行 Cleanup,另外还有几种情况也是会触发这个步骤的执行的:
G1 采用的是复制方法来进行收集,必须保证每次的”to space”的空间都是够的,因此 G1 采取的 策略是当已经使用的内存空间达到了 H 时,就执行 Cleanup 这个步骤;
对于 full-young 和partially-young 的分代模式的 G1 而言,则还有情况会触发 Cleanup 的执行,
full-young 模式下,G1 根据应用可接受的暂停时间、回收 young regions 需要消耗的时间来估算出 一个 yound regions 的数量值,当 JVM 中分配对象的 young regions 的数量达到此值时,Cleanup 就会执行;
partially-young 模式下,则会尽量频繁的在应用可接受的暂停时间范围内执行 Cleanup, 并最大限度的去执行 non-young regions 的Cleanup。
JVM垃圾回收_丢丢丢Dr.的博客-CSDN博客_jvm垃圾回收
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。