首页系统综合问题「垃圾回收」百度面试被问到cms垃圾回收机制,麻了麻了

「垃圾回收」百度面试被问到cms垃圾回收机制,麻了麻了

时间2023-02-08 09:15:36发布分享专员分类系统综合问题浏览115

今天小编给各位分享enabled的知识,文中也会对其通过「垃圾回收」百度面试被问到cms垃圾回收机制,麻了麻了和5、垃圾回收机制等多篇文章进行知识讲解,如果文章内容对您有帮助,别忘了关注本站,现在进入正文!

内容导航:

  • 「垃圾回收」百度面试被问到cms垃圾回收机制,麻了麻了
  • 5、垃圾回收机制
  • JVM 垃圾回收( CMS 和 G1 )篇
  • C#垃圾回收机制(GC)
  • 一、「垃圾回收」百度面试被问到cms垃圾回收机制,麻了麻了

    【垃圾回收】系列(@.@)

    [cms垃圾回收机制]

    最近在整理JVM相关的PPT,把CMS算法又过了一遍,每次阅读源码都能多了解一点,继续坚持。

    什么是CMS

    CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,如果老年代使用CMS垃圾回收器,需要添加虚拟机参数-"XX:+UseConcMarkSweepGC"。

    使用场景:

    GC过程短暂停,适合对时延要求较高的服务,用户线程不允许长时间的停顿。

    缺点:

    服务长时间运行,造成严重的内存碎片化。另外,算法实现比较复杂(如果也算缺点的话)

    实现机制

    根据GC的触发机制分为:周期性Old GC(被动)和主动Old GC个人理解,实在不知道怎么分才好。

    周期性Old GC

    周期性Old GC,执行的逻辑也叫Background Collect,对老年代进行回收,在GC日志中比较常见,由后台线程ConcurrentMarkSweepThread循环判断(默认2s)是否需要触发。

    触发条件

    1、如果没有设置-XX:+UseCMSInitiatingOccupancyOnly,虚拟机会根据收集的数据决定是否触发(建议线上环境带上这个参数,不然会加大问题排查的难度)。2、老年代使用率达到阈值 CMSInitiatingOccupancyFraction,默认92%。3、永久代的使用率达到阈值 CMSInitiatingPermOccupancyFraction,默认92%,前提是开启 CMSClassUnloadingEnabled。4、新生代的晋升担保失败。

    晋升担保失败

    老年代是否有足够的空间来容纳全部的新生代对象或历史平均晋升到老年代的对象,如果不够的话,就提早进行一次老年代的回收,防止下次进行YGC的时候发生晋升失败。

    周期性Old GC过程

    当条件满足时,采用“标记-清理”算法对老年代进行回收,过程可以说很简单,标记出存活对象,清理掉垃圾对象,但是为了实现整个过程的低延迟,实际算法远远没这么简单,整个过程分为如下几个部分:

    对象在标记过程中,根据标记情况,分成三类:

    白色对象,表示自身未被标记;灰色对象,表示自身被标记,但内部引用未被处理;黑色对象,表示自身被标记,内部引用都被处理;

    假设发生Background Collect时,Java堆的对象分布如下:

    1、InitialMarking(初始化标记,整个过程STW)

    该阶段单线程执行,主要分分为两步:

    标记GC Roots可达的老年代对象;遍历新生代对象,标记可达的老年代对象;

    该过程结束后,对象分布如下:

    image

    2、Marking(并发标记)

    该阶段GC线程和应用线程并发执行,遍历InitialMarking阶段标记出来的存活对象,然后继续递归标记这些对象可达的对象。

    因为该阶段并发执行的,在运行期间可能发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。

    为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代。

    image

    3、Precleaning(预清理)

    通过参数CMSPrecleaningEnabled选择关闭该阶段,默认启用,主要做两件事情:

    处理新生代已经发现的引用,比如在并发阶段,在Eden区中分配了一个A对象,A对象引用了一个老年代对象B(这个B之前没有被标记),在这个阶段就会标记对象B为活跃对象。在并发标记阶段,如果老年代中有对象内部引用发生变化,会把所在的Card标记为Dirty(其实这里并非使用CardTable,而是一个类似的数据结构,叫ModUnionTalble),通过扫描这些Table,重新标记那些在并发标记阶段引用被更新的对象(晋升到老年代的对象、原本就在老年代的对象)4、AbortablePreclean(可中断的预清理)

    该阶段发生的前提是,新生代Eden区的内存使用量大于参数CMSScheduleRemarkEdenSizeThreshold 默认是2M,如果新生代的对象太少,就没有必要执行该阶段,直接执行重新标记阶段。

    为什么需要这个阶段,存在的价值是什么?

    因为CMS GC的终极目标是降低垃圾回收时的暂停时间,所以在该阶段要尽最大的努力去处理那些在并发阶段被应用线程更新的老年代对象,这样在暂停的重新标记阶段就可以少处理一些,暂停时间也会相应的降低。

    在该阶段,主要循环的做两件事:

    处理 From 和 To 区的对象,标记可达的老年代对象和上一个阶段一样,扫描处理Dirty Card中的对象

    当然了,这个逻辑不会一直循环下去,打断这个循环的条件有三个:

    可以设置最多循环的次数 CMSMaxAbortablePrecleanLoops,默认是0,意思没有循环次数的限制。如果执行这个逻辑的时间达到了阈值CMSMaxAbortablePrecleanTime,默认是5s,会退出循环。如果新生代Eden区的内存使用率达到了阈值CMSScheduleRemarkEdenPenetration,默认50%,会退出循环。(这个条件能够成立的前提是,在进行Precleaning时,Eden区的使用率小于十分之一)

    如果在循环退出之前,发生了一次YGC,对于后面的Remark阶段来说,大大减轻了扫描年轻代的负担,但是发生YGC并非人为控制,所以只能祈祷这5s内可以来一次YGC。

    ... 1678.150: [CMS-concurrent-preclean-start] 1678.186: [CMS-concurrent-preclean: 0.044/0.055 secs] 1678.186: [CMS-concurrent-abortable-preclean-start] 1678.365: [GC 1678.465: [ParNew: 2080530K->1464K(2044544K), 0.0127340 secs] 1389293K->306572K(2093120K), 0.0167509 secs] 1680.093: [CMS-concurrent-abortable-preclean: 1.052/1.907 secs]  ....

    在上面GC日志中,1678.186启动了AbortablePreclean阶段,在随后不到2s就发生了一次YGC。

    5、FinalMarking(并发重新标记,STW过程)

    该阶段并发执行,在之前的并行阶段(GC线程和应用线程同时执行,好比你妈在打扫房间,你还在扔纸屑),可能产生新的引用关系如下:

    老年代的新对象被GC Roots引用老年代的未标记对象被新生代对象引用老年代已标记的对象增加新引用指向老年代其它对象新生代对象指向老年代引用被删除也许还有其它情况..

    上述对象中可能有一些已经在Precleaning阶段和AbortablePreclean阶段被处理过,但总存在没来得及处理的,所以还有进行如下的处理:

    遍历新生代对象,重新标记根据GC Roots,重新标记遍历老年代的Dirty Card,重新标记,这里的Dirty Card大部分已经在clean阶段处理过

    在第一步骤中,需要遍历新生代的全部对象,如果新生代的使用率很高,需要遍历处理的对象也很多,这对于这个阶段的总耗时来说,是个灾难(因为可能大量的对象是暂时存活的,而且这些对象也可能引用大量的老年代对象,造成很多应该回收的老年代对象而没有被回收,遍历递归的次数也增加不少),如果在AbortablePreclean阶段中能够恰好的发生一次YGC,这样就可以避免扫描无效的对象。

    如果在AbortablePreclean阶段没来得及执行一次YGC,怎么办?

    CMS算法中提供了一个参数:CMSScavengeBeforeRemark,默认并没有开启,如果开启该参数,在执行该阶段之前,会强制触发一次YGC,可以减少新生代对象的遍历时间,回收的也更彻底一点。

    不过,这种参数有利有弊,利是降低了Remark阶段的停顿时间,弊的是在新生代对象很少的情况下也多了一次YGC,最可怜的是在AbortablePreclean阶段已经发生了一次YGC,然后在该阶段又傻傻的触发一次。

    所以利弊需要把握。

    主动Old GC

    这个主动Old GC的过程,触发条件比较苛刻:

    YGC过程发生Promotion Failed,进而对老年代进行回收比如执行了System.gc(),前提是没有参数ExplicitGCInvokesConcurrent其它情况...

    如果触发了主动Old GC,这时周期性Old GC正在执行,那么会夺过周期性Old GC的执行权(同一个时刻只能有一种在Old GC在运行),并记录 concurrent mode failure 或者 concurrent mode interrupted。

    主动GC开始时,需要判断本次GC是否要对老年代的空间进行Compact(因为长时间的周期性GC会造成大量的碎片空间),判断逻辑实现如下:

    *should_compact = UseCMSCompactAtFullCollection && ((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction) || GCCause::is_user_requested_gc(gch->gc_cause()) || gch->incremental_collection_will_fail(true /* consult_young */));

    在三种情况下会进行压缩:

    其中参数UseCMSCompactAtFullCollection(默认true)和 CMSFullGCsBeforeCompaction(默认0),所以默认每次的主动GC都会对老年代的内存空间进行压缩,就是把对象移动到内存的最左边。当然了,比如执行了System.gc(),前提是没有参数ExplicitGCInvokesConcurrent,也会进行压缩。如果新生代的晋升担保会失败。

    带压缩动作的算法,称为MSC,标记-清理-压缩,采用单线程,全暂停的方式进行垃圾收集,暂停时间很长很长...

    那不带压缩动作的算法是什么样的呢?

    不带压缩动作的执行逻辑叫Foreground Collect,整个过程相对周期性Old GC来说,少了Precleaning和AbortablePreclean两个阶段,其它过程都差不多。

    如果执行System.gc(),而且添加了参数ExplicitGCInvokesConcurrent,这时并不属于主动GC,它会推进周期性Old GC的进行,比如刚刚执行过一次,并不会等2s后检查条件,而是立马启动周期性Old GC。

    最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。

    求三连!!!

    一、5、垃圾回收机制

    JVM的垃圾回收机制主要涉及三个方面的问题:
    1.JVM有哪些垃圾回收算法?各自有什么优势?
    2.CMS垃圾回收器是如何工作的?有哪些阶段?
    3.服务卡顿的元凶到底是什么?
    Java不用程序来管理内存的回收,但这些内存是如何回收的?
    其实,JVM有专门的线程在做这件事情。当内容空间达到一定条件时,会自动触发,这个过程就叫GC,负责GC的组件被称为垃圾回收器。JVM规范没有规定垃圾回收器怎么实现,它只需要保证不要把正在使用的对象回收掉就可以。在现在的服务器环境中,经常被使用的垃圾回收器有CMS和G1,但JVM还有其它几个常见的垃圾回收器。
    GC的过程是先找到活跃的对象,然后把其他不活跃的对象判定为垃圾,然后删除,所以GC只与活跃的对象有关,和堆的大小无关。
    接下来学习下分代垃圾回收的内存划分和GC过程,再有就是常见的垃圾回收器。
    这篇比较重要,因为几乎所有的垃圾回收器都是在这些基本思想上演化出来的。

    GC的第一步就是找出活跃的对象,根据GC Roots遍历所有的可达对象,这个过程就叫作标记。

    如上图所示,圆圈代表对象,绿色的代表GC Roots,红色的代表可以追溯到的对象,标记后,有多个灰色的圆圈,代表都是可被回收的对象。

    清除阶段就是把未被标记的对象回收掉。
    这种方式有一个明显的问题,会产生碎片空间。
    比如申请了1k、2k、3k、4k、5k的内存

    由于某些原因,2k和4k的内存不再使用,交给垃圾回收器回收。

    解决碎片问题,就需要进行内存整理。
    有一个思路就是提送一个对等的内存空间,将存活的对象复制过去,然后清除员内存空间。
    在程序设计时,一般遇到扩缩容或者碎片整理问题时,复制算法都是非常有效的。比如:HashMap的扩容使用的是同样的思路,Redis的rehash也是如此。
    整个过程如下图

    这种方式看似完美,解决了碎片问题,但是弊端也非常明显,它浪费了一半的内存空间来做这个事情,如果原本资源就有限,这就是一种无法容忍的浪费。

    不用分配一个对等的空间也是可以完成内存的整理工作。
    可以把内存想象成一个非常大的数组,根据随机的index删除了一些数据,那么对数组的清理不需要另外一个数组来进行支持的,使用程序就可以。
    主要思路是移动所有的存活对象,且按照内存地址顺序依次排列,然后将末端内存地址以后的内存全部收回。

    对象的引用关系一般是非常复杂的,从效率上来说,一般整理算法是要低于复制算法的。
    JVM的垃圾回收器,都是对以上几种朴素算法的结合使用,简单看一下它们的特点:

    效率一般,缺点是回造成内存碎片的问题。

    复制算法是所有算法里面效率最高的,缺点是造成一定的空间浪费。

    效率比前两者要差,但没有空间浪费,也消除了内存碎片问题。

    所以没有最优的算法,只有最合适的算法。

    JVM是计算节点,而不是存储节点。最理想的情况就是对象使用完成之后,它的生命周期立马就结束了,而那些被频繁访问的资源,我们希望它能够常驻在内存里。
    对象大致可以分为两类:
    1.大部分对象的生命周期都很短
    2.其他对象则很可能会存活很长时间

    现在的垃圾回收器都会在物理上或者逻辑上,把这两类对象进行分区。我们把死的快的对象所占的区域叫年轻代(Young Generation)。把其他活的长的对象所占的区域叫作老年代(Old Generation),老年代在有时候会叫作Tenured Generation。

    年轻代使用的垃圾回收算法是复制算法,因为年轻代发生GC后,会有非常少的对象存活,复制这部分对象是非常高效的
    年轻代的内部分区

    如图所示,年轻代分为:一个伊甸园空间(Eden),两个幸存者空间(Survivor)。
    当年轻代中的Eden区分配满的时候,就会触发年轻代的GC(Minor GC),具体过程如下
    1.在Eden区执行了第一次GC之后,存活的对象会被移动到其中一个Suvivor分区(from);
    2.Eden区再次GC,这是会采用复制算法,将Eden和from区一起清理,存活的对象会被复制到to区;接下来只需要清空from区就可以了
    在整个过程中总会有一个Survivor分区是空置的。Eden、from、to的默认比例是8:1:1,所以只会造成10%的空间浪费。
    这个比例是由参数-XX:SurvivorRatio进行配置的(默认为8)。
    补充下不常提到的TLAB。TLAB全称是Thread Local Allocation Buffer,JVM默认给每个线程开辟一个buffer区域,用来加速对象分配。这个buffer就放在Eden区中。
    这个道理和Java语言中的ThreadLocal类似,避免了对公共区的操作,以及一些锁竞争。

    老年代一般使用"标记-清除"、"标记-整理"算法。因为老年代的对象存活率一般是比较高的,空间又比较大,拷贝起来并不划算,不如采取就地收集的方式。
    对象进入老年代的途径分类

    如果对象够老,会通过"提升"进入老年代。关于对象老不老,是通过它的年龄来判断的。每发生一次Minor GC,存活下来的对象年龄都会加1,直到达到一定的阀值,就会提升到老年代,
    这些对象如果变的不可达,直到老年代发生GC的时候才会被清理掉。
    这个阀值可以通过参数 -XX:+MaxTenuringThreshold进行配置,最大值是15,因为它是用4bit存储的(所以把这个值调的很大的文章,是没有什么根据的)。

    每次存活的对象,都会放入其中一个幸存区,这个区域默认比例是10%,但无法保证每次存活的对象都小于10%,当Survivor空间不够,就需要依赖其它内存(老年代)进行分配担保。这个时候,对象也会直接在老年代上分配。

    超出某个大小的对象直接在老年代分配,通过参数设置-XX:PretenureSizeThreshold进行配置的,默认为0,默认全部在Eden区进行分配。

    有的垃圾回收算法,并不要求age必须达到15才能晋升到老年代,它会使用一些动态的计算方法。比如,如果幸存区中相同年龄对象大小的和,大于幸存区的一半,大于或者等于age的对象将会直接进入老年代。
    这些动态判定一半不受外部控制

    对象的引用关系时一个巨大的网状,有的对象在Eden区,有的可能在老年代,那么这种跨代的引用是如何处理的呢?由于Minor GC是单独发生的,如果一个老年代的对象引用了它,如何确保能够让年轻代的对象存活呢?
    对于是、否的判断,我们通常都会用到Bitmap(位图)和布隆过滤器来加快搜索的速度,需要另外再学习下(如果不知道这两个概念的话)
    JVM也是用了类似的方法。其实,老年代是被分成众多的卡页(Card Page)的(一般数量是2的次幂)
    卡表(Card Table)就是用于标记卡页状态的一个集合,每个卡表对应一个卡页。
    如果年轻代有对象分配,而且老年代有对象指向这个新对象,那么这个老年代对象所对应内存的卡页就会被标识为dirty,卡表只需要非常小的存储空间就可以保留这些状态,垃圾回收时,就可以先读这个卡表,进行快速的判断。

    接下来学习HotSpot的几个垃圾回收器,每种回收器都有各自的特点。在平常的GC优化时,一定要清楚现在用的是那种垃圾回收器。
    下图包含了年轻代和老年代的划分,方便接下来的学习参考

    处理GC的只有一条线程,并且在垃圾回收的过程中暂停一切用户线程。
    这是最简单的垃圾回收器,虽然简单,但十分高效,通常用在客户端应用上。因为客户端应用不会频繁创建很多对象,用户也不会感觉出明显的卡顿。相反,它使用的资源更少,也更轻量级。

    ParNew是Serial的多线程版本,由多条GC线程并行地进行垃圾清理。清理过程依然要停止用户线程。追求低停顿时间,与Serial唯一区别就是使用了多线程进行垃圾回收,在多CPU环境下性能比Serial会有一定程度的提升;但线程切换需要额外的开销,因此在单CPU环境中表现不如Serial。

    另一个多线程版本的垃圾回收器。但与ParNew是有区别的
    1.Parallel Scavenge:追求CPU吞吐量,能够在较短时间内完成指定任务,适合没有交互的后台计算,弱交互强计算。
    2.ParNew:追求降低用户停顿时间,适合交互式应用,强交互弱计算。

    与年轻代的Serial垃圾回收器对应,都是单线程版本,同样适合客户端使用。
    年轻代Serial,使用复制算法。
    老年代的Old Serial,使用标记-整理算法。

    Parallel Old回收器是Parallel Scavenge 的老年代版本,追求CPU吞吐量。

    CMS(Concurrent Mark Sweep)回收器是以获取最短GC停顿时间为目标的收集器,它在垃圾回收时使得用户线程和GC线程能够并发执行,因此在垃圾回收过程中用户也不会感到明显的卡顿。
    长期看来,CMS垃圾回收器,是要被G1等垃圾回收器替换掉的,在Java8之后,使用它将会抛出一个警告!

    除了上面几个垃圾回收器,我们还有G1、ZGC等更加高级的垃圾回收器,它们都有专门的配置参数来使其生效。
    通过-XX:PrintCommandLineFlags参数,可以查看当前Java版本默认使用的垃圾回收器。在Java13中,默认的回收器就是G1。
    以下是一些配置参数:
    1.-XX:+UseSerialGC 年轻代和年老代回收器
    2.-XX:+UseParNewGC 年轻代使用ParNew,老年代使用Serial Old。
    3.-XX:+UseParallelOldGC 年轻代和老年代哦都市用并行回收器。
    4.-XX:+UseConcMarkSweepGC 表示年轻代使用ParNew,老年代使用CMS。
    5.-XX:+UseG1GC 使用G1垃圾回收器
    6.-XX:+UseZGC 使用ZGC垃圾回收器

    这些垃圾回收器的关系还是比较复杂的,请看下图

    目前Java8还是主流使用版本,从Java8升级到高版本的Java体系是有一定成本的,所以CMS垃圾回收器还会持续一段时间

    抛个问题,如果在垃圾回收的时候,又有新的对象进入怎么办?
    为了保住程序不乱套,最好的办法就是暂停用户的一切线程,也就是在这段时间,是不能new对象的,只能等待,表象是在JVM上就是短暂的卡顿,什么都干不了,这个现象叫作Stop The World。
    标记阶段,大多数是要STW的。如果不暂停用户进程,在标记对象的时候,有可能有其它用户线程会产生一些新的对象和引用,造成混乱。
    现在的垃圾回收器,都会尽量去减少这个过程。但即使最先进的ZGC回收器,也会有短暂的STW过程。我们要做的就是在现有基础设施上,尽量减少GC停顿。
    举例说明下
    某个高并发服务的峰值流量是10万次/秒,后面有10台负载均衡的机器,那么每台机器平均下来需要1w/s。假如某台机器在这段时间内发生了STW,持续了一秒,那么至少需要10ms就可以返回的1万个请求,需要至少等待1秒。

    在用户那里的表现就是系统发生了卡顿。如果我们的GC非常的频繁。这种卡顿就会特别的明显,严重影响用户体验。
    虽然说Java为我们提供了非常棒的自动内存管理机制,但也不能滥用,因为它是有STW硬伤的。

    介绍了堆的具体分区,年轻代和老年代。介绍了多个常用的垃圾回收器,不同的垃圾回收器有不同的特点。各种垃圾回收器都是为了解决头疼的STW问题,让GC时间更短,停顿更短,吞吐量更大。
    接触了很多名词,总结如下

    1.Mark
    2.Sweep
    3.Copy
    4.Compact

    1.Young generation
    2.Survivor
    3.Eden
    4.Old Generation |Tenured Generation
    5.GC
    --1.Minor GC
    --2.Major GC

    1.weak generational hypothesis
    2.分配担保
    3.提升
    4.卡片标记
    5.STW

    二、JVM 垃圾回收( CMS 和 G1 )篇

    GC Roots 对象的包括如下几种:

    当 GC 线程进行并发操作时,应用程序可能会进行新增对象、删除对象、变更对象引用等一系列操作。这种条件下可能会出现活动对象的漏标的情况,

    比如下面场景:

    为了解决这个问题,还需要额外的操作,这个操作就是 write barrier。

    在使用 Write bariier 之后同样的情景就不会出现活动对象被遗漏的情况了.

    YGC 时为了标记活动标记对象除了 tracing GC ROOTS 之外,老年代里也可能会引用新生代对象。
    所以正常来说还要扫描一次老年代,如果是扫描整个老年代这将会随着堆的增大变得越来越慢,特别是现在内存都越来越大了。所以为了提升性能就引入卡表。

    卡表提升性能的原理 :逻辑上把老年代内存分成一个个大小相等的卡片,然后对每个卡片准备一个与其对应的标记位,并将这些位集中起管理就好像一个表格 (mark table) 一样,当改写对象引用是从老年代指向新生代时,在老年代对应的卡片标记位上设置标志位即可,通常这样的卡片我们称之为 dirty card。

    这项操作可以通过上面的提到的 write barrier 来实现,这样就算对象跨多张卡片也不会有什么问题。 卡表通常是用 byte 数组实现的,byte 的值只能取 [0,1] 这两种。所以 btye [i] = 1 就表示第 i + 1 卡片所在内存上有指向新生代引用的老年代对象,这时只要 tracing 这个卡片上的对象即可。背后思想就是典型以空间换时间的思路!

    G1 将整个堆划分为一个个大小相等的小块(每一块称为一个 region),每一块的内存是连续的。和分代算法一样,G1 中每个块也会充当 Eden、Survivor、Old 三种角色,但是它们不是固定的,这使得内存使用更加地灵活。

    G1 收集器主要包括了以下 4 种操作:

    全局并发标记过程分为五个阶段

    (1) 初始标记

    (2) Root Region Scanning 根区域扫描

    (3) Concurrent Marking 并发标记

    (4) Remark 最终标记

    (5) Cleanup 清除

    主要完成了垃圾定位的工作,定位出了哪些分区是垃圾最多的。

    并发周期结束后是混合垃圾回收周期,不仅进行年轻代垃圾收集,而且回收之前标记出来的老年代的垃圾最多的部分区块。

    MIXED GC 周期会持续进行,直到几乎所有的被标记出来的分区(垃圾占比大的分区)都得到回收,然后恢复到常规的年轻代垃圾收集,最终再次启动并发周期。

    下面我们来介绍特殊情况,那就是会导致 Full GC 的情况,也是我们需要极力避免的:

    把 Ruser 和 Rcon 合并一下,形成一个新的,完整的可到达对象关系 Rfinal,交给 GC 程序。

    CMS 和 G1 都采取一种方式 Write barrier+log,3 个步骤:

    Rslog 的作用就是记录用户程序对对象关系的修改;
    用户程序的修改只能有 2 种:

    总结: 宁可放过,下一次处理,也不错杀

    参考: CMS 收集器原理理解与分析
    参考: G1 收集器原理理解与分析

    三、C#垃圾回收机制(GC)

    GC是一个垃圾回收机制 它主要是回收 托管对象 而不会回收 非托管对象 就像你使用某些非托管数据库链接对象的时候 就需要手动关闭 这些需要手动关闭的对象就是非托管对象 而这个就不是在GC管理范围之内
    另外要说一下的是 GC这个东西很调皮 有时候GC的回收是没有固定时间的 随机的 所以 有时候我们需要手动关闭一些比较大的托管对象来提高性能

    关于enabled的问题,通过《JVM 垃圾回收( CMS 和 G1 )篇》、《C#垃圾回收机制(GC)》等文章的解答希望已经帮助到您了!如您想了解更多关于enabled的相关信息,请到本站进行查找!

    爱资源吧版权声明:以上文中内容来自网络,如有侵权请联系删除,谢谢。

    enabled
    电脑键盘失灵怎么办 三张图带你提高电脑启动速度