目录

一、垃圾回收GC (Garbage Collection)

1.1 垃圾回收介绍:

1.2  如何判断对象是否存活(被使用)

1.2.1 引用计数算法(已经废弃)

1.2.2 可达性分析

二、垃圾回收基本算法

2.1 标记-清除(Mark-Sweep)

2.2 复制(Copying):

2.3 标记-整理(Mark-Compact):

2.4. Generational Collection(分代收集)算法

2.4.1 Eden(伊甸园区):

2.4.2 YGC整个流程:

2.4.3  Full GC

三、垃圾回收器

3.1 各个垃圾回收器

3.2  Serial/Serial Old 串行收集器(-XX:+UseSerialGC)

3.3并行收集器( -XX:+UseParallelGC、-XX:+UseParallelOldGC)

3.4 并发收集器/CMS收集器 Concurrent Mark Sweep

3.5 、Garbage Firest(G1)

3.5 .1算法详解:

3. 5.2 回收步骤:

四、参考:



一、垃圾回收GC (Garbage Collection)

1.1 垃圾回收介绍:

GC (Garbage Collection:即垃圾回收):将内存中不再被使用的对象(非存活对象)进行回收,GC中用于回收的方法称为收集器。

1.2  如何判断对象是否存活(被使用)

当垃圾收集器在对堆进行回收前,第一就是要确定对象哪些是还在被引用的或者后面还需要被引用的,即存活,哪些是已经“死去”(即不可能再被任何途径使用)。回收的就是这些非存活的对象。

1.2.1 引用计数算法(已经废弃)

  引用计数是垃圾收集器中的早期策略。堆中每个对象实例都有一个引用计数,当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。每当有一个地方引用它时,计数器值就加1,引用失效时就减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。

缺点:

很难解决对象之间相互循环引用的问题,就如果两个对象之间相互调用,这个地方相互调用会使得引用计数法始终认为有对象在引用当前对象,就一直计数值大于或等于1,也就无法通知GC收集器回收它们。但是实际的情况是这两个对象后面已经不再调用。

1.2.2 可达性分析

可达性算法是目前主流的虚拟机都采用的算法,程序把所有的引用关系看作一张图,从一个节点GC Roots开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。

二、垃圾回收基本算法

1.Mark-Sweep(标记-清除)算法

2.Copying(复制)算法

3.Mark-Compact(标记-整理)算法(压缩法)

4.Generational Collection(分代收集)算法

2.1 标记-清除(Mark-Sweep)

https://www.html.cn/qa/other/22924.html

此算法执行分两阶段。

第一阶段 标记:遍历内存区域,对需要回收的对象打上标记。

第二阶段 清除:再次遍历内存,对已经标记过的内存进行回收。

此算法需要暂停整个应用

绿色、蓝色---代表存活对象

灰色---非存活对象

白色--未使用的内存

缺点:

该算法最大的问题是内存碎片化严重,后续可能发生大对象不能找到可利用空间的问题。

效率问题:遍历了两次内存空间(第一次标记,第二次清除)。

空间问题:gc之后,会产生大量的内存碎片,随着gc次数增多,未使用的内存会被切割的越来越小。这些零碎的内存,足够小时,就不能存放连续的内存(如数组,数组是连续的内存空间),如数组10m,但是这一块内存只有1m,虽然这块内存未使用,但是也不能用来存放这个10m的数组。这种零碎的内存越来越多,则整体可以使用的内存越来越少。当再需要一块比较大的内存时,无法找到一块满足要求的,因而不得不再次出发GC。

2.2 复制(Copying):

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。

垃圾回收时,遍历当前使用区域, 把正在使用中的对象复制到另外一个区域中。

优点:此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。

缺点:最大的缺点就是需要两倍内存空间。且存活对象增多的话,Copying算法的效率会大大降低。

jvm执行YGC时,清理伊甸园区和两个存活区就使用的复制算法。

老年代默认使用的是 标记-清除 算法,不可以使用复制算法,使用标记-清除算法,内存都可以被回收掉,但是不能清除碎片内存。

在这两个算法之上,又出现了 标记-整理(Mark-Compact)算法,结合了两种算法的优点。

2.3 标记-整理(Mark-Compact):

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,

第一阶段标记所有的存活对象

第二阶段遍历整个堆,清除非存活的对象并且把存活对象“压缩”到堆的其中一 块,按顺序排放。

此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

FGC,清理老年代时,期望使用标记-整理算法,

2.4. Generational Collection(分代收集)算法

分代收集算法是目前大部分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大小相等,位置互换

堆外(放的是方法,类,常量)

2.4.1 Eden(伊甸园区):

所有新new的对象和数组,是在伊甸园区产生的,内存分配是在伊甸园区进行的。

当伊甸园区内存被占满时,就不能再new对象,程序依赖对象,所以程序也就不能运行。java应用程序为了能继续运行,就要干掉那些不被引用的对象,对这些不被引用的对象进行垃圾回收。

当伊甸园区被占满时,触发GC(垃圾回收 Garbage Collection)。

GC分两步,第1步标记,对非存活对象进行标记。第2步清扫,将非存活对象从内存中干掉,释放内存,同时将存活的对象移到存活区中。

YGC

发生在年轻代。

在YGC之前,伊甸园区是满的,无法new对象,程序无法运行。

YGC之后,伊甸园区就空了,就可以继续new对象。

2.4.2 YGC整个流程:

第一次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存放对象。

对象进入老年代原则:

  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
  • 动态年龄判断
  • 一次Young GC时数据放到存活区,但是存活区满了导致放不下去此时直接进入老年代(尽可能避免这种情况)

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区到老年代,那么存活区就没有什么存在的意义了,之所以设置存活区,就是为了将没有引用的对象更早的回收掉,将内存腾出来。

2.4.3  Full GC

当老年代满了之后,触发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和别的优秀的老年代收集器不搭。出现后他俩搭配,才让吞吐量优先的收集器名副其实。

3.1 各个垃圾回收器

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、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

3.2  Serial/Serial Old 串行收集器(-XX:+UseSerialGC)

用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。

但是,也无法使用多处理 器的优势,所以此收集器适合单处理器机器。

当然,此收集器也可以用在小数据量(100M 左右)情况下的多处理器机器上。

可以使用-XX:+UseSerialGC 打开。

年轻代YGC

串行垃圾回收,如图一共4个cpu,YGC用一个线程来进行垃圾回收(寻根判断--复制算法),

YGC时只有一个线程进行垃圾收集,此时应用程序处于暂停状态,不能工作。

老年代FGC

串行收集,标记-整理算法,一个线程进行垃圾收集,只有一个线程干活,效率比较低。

串行收集这种效率比较低,基本上已经废弃了。如果没有单独配置垃圾回收策略,1.7及之前的JDK,默认用的就是这种垃圾回收器。

1.8JDK如果不配置垃圾回收器,默认是并行收集器。

3.3并行收集器( -XX:+UseParallelGC、-XX:+UseParallelOldGC)

年轻代,执行YGC时,多个线程进行寻根判断,标记存活对象,多个线程进行通过复制算法进行垃圾回收,可以发挥多核cpu的优势,效率比较高。

但是老年代依然使用的串行收集,所以老年代还是比较慢。

对年轻代迕行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用 -XX:+UseParallelGC.打开。

并行收集器在 J2SE5.0 第六 6 更新上引入,在 Java SE6.0 中迕行了增强 --可以对年老代迕行并行收集。

如果年老代不使用并发收集的话,默认是使用单线程进行垃圾回收, 因此会制约扩展能力。使用-XX:+UseParallelOldGC 打开。

所以:

如果只配置 -XX:+UseParallelGC,只是单纯的并行年轻代。

打开-XX:+UseParallelOldGC,就可以同时并行老年代了

使用-XX:ParallelGCThreads=设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相 等。

并行收集器,年轻代并行收集、老年代并行收集时,都会暂停应用程序,如果内存足够大,暂停时间依然很长。

3.4 并发收集器/CMS收集器 Concurrent Mark Sweep

CMS垃圾收集器深入详解 - cexo - 博客园

并发收集器简称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)。

年轻代-多个线程进行垃圾回收工作,暂停应用程序,但是暂停时间会比较短。

在每个老年代垃圾回收周期中,在收集初期并发收集器 会对整个应用进行简短的暂停,在 收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。

3.5 、Garbage Firest(G1)

原文链接: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)收集的方式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。

3.5 .1算法详解:

G1 可谓博采众家之长,力求到达一种完美。他吸取了增量收集优点,把整个堆划分为一个一个等大 小的区域(region)。

内存的回收和划分都以 region 为单位;同时,他也吸取了 CMS 的特点,把 这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;而且,G1 也认同分代垃圾回收的思想, 认为不同对象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。

为了达到 对回收时间的可预计性,G1 在扫描了 region 以后,对其中的活跃对象的大小进行排序,首先会收集 那些活跃对象小的 region,以便快速回收空间(要复制的活跃对象少了),因为活跃对象小,里面 可以认为多数都是垃圾,所以这种方式被称为 Garbage First(G1)的垃圾回收算法,即:垃圾优先 的回收。

3. 5.2 回收步骤:

初始标记(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垃圾回收算法 - 方块人 - 博客园

JVM垃圾回收_丢丢丢Dr.的博客-CSDN博客_jvm垃圾回收

垃圾回收算法_会飞的IT蜗牛的博客-CSDN博客_垃圾回收算法

JVM-简介&垃圾回收&内存泄漏分析_傲娇的喵酱的博客-CSDN博客_伊甸园区

JVM垃圾回收算法及垃圾回收器相关推荐

  1. 垃圾回收算法以及垃圾回收器_什么是垃圾回收?

    垃圾回收算法以及垃圾回收器 以下是我们的垃圾收集手册中的一个示例,该手册将在接下来的几周内发布. 同时,花点时间熟悉垃圾收集的基础知识-这将是本书的第一章. 乍一看,垃圾收集应该处理顾名思义的问题–查 ...

  2. 【11-JVM面试专题-说说你知道的垃圾回收算法?垃圾回收器你知道吗?CMS、G1和ZGC垃圾回收器你有过了解吗?】

    JVM面试专题-说说你知道的垃圾回收算法?垃圾回收器你知道吗?CMS.G1和ZGC垃圾回收器你有过了解吗? JVM面试专题-说说你知道的垃圾回收算法?垃圾回收器你知道吗?CMS.G1和ZGC垃圾回收器 ...

  3. 2 自动内存管理机制(一)运行时数据区域、垃圾回收算法和垃圾回收器

    文章目录 自动内存管理机制(一)运行时数据区域.垃圾回收算法和垃圾回收器 运行时数据区域 垃圾收集算法(方法论) 垃圾收集器(具体实现) 附录 响应时间和吞吐量 参考 自动内存管理机制(一)运行时数据 ...

  4. JVM架构、JVM垃圾回收机制、垃圾回收算法、垃圾回收器、JMM(内存模型)

    0 JVM和Java的关系 JDK = JRE + Java开发工具(java,javac,javadoc,javap-) JRE = JVM + Java核心类库 即: JDK = JVM + Ja ...

  5. 胡说八道JVM—垃圾回收算法和垃圾回收器

    垃圾回收算法 引用计数器法(Reference Counting) 可达性分析 标记清除算法(Mark-Sweep) 这个算法的原理很简单,但是它却是其他算法的基础,后续的其他算法否是在这个算法的基础 ...

  6. jvm垃圾回收算法和垃圾回收器

    垃圾回收算法 jvm垃圾回收算法包括复制算法.标记清楚算法和标记整理算法,它们都基于分代收集理论.所谓分代收集理论,可以理解为jvm根据对象的生命年龄将他们分在不同的内存模块,也就是熟知的新生代和老年 ...

  7. JVM学习笔记(二):垃圾回收、垃圾回收算法、垃圾回收器(Serial、Parallel、CMC、G1)、内存分配原则实战

    垃圾回收 一.判断对象是否可以被回收 1.引用计数计数法 内容:在对象中添加一个引用计数器,每当有一个地方引用它,计数器就加一:当引用失效时,计数器就减一:任何时刻计数器为零的对象都是不可能在被使用的 ...

  8. 垃圾回收算法与垃圾回收器

    Java与C++等语言最大的技术区别:自动化的垃圾回收机制(GC) 为什么要了解GC和内存分配策略 1.面试需要 2.GC对应用的性能是有影响的: 3.写代码有好处 栈:栈中的生命周期是跟随线程,所以 ...

  9. 直通BAT必考题系列:JVM的4种垃圾回收算法、垃圾回收机制与总结

    BAT必考JVM系列专题 直通BAT必考题系列:深入详解JVM内存模型与JVM参数详细配置  垃圾回收算法 1.标记清除 标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段. 在标记阶段首先通过 ...

最新文章

  1. html 关闭js控件,javascript – 用JS关闭html5视频控件
  2. gin 获取post请求的json body操作详解
  3. 无root权限新建git仓库进行多人协同工作
  4. oracle 按月累计求和,SQL Cumulative Sum累积求和
  5. Linux系统安全学习手册
  6. 微信公众号文章中图片加载时,占位图宽高大小的确定
  7. mysql中如何删除表中int约束,MySQL中的约束,添加约束,删除约束,以及其他修饰
  8. 《Android 3D游戏开发技术宝典——OpenGL ES 2.0》——2.4节文件I/O
  9. blog.mm index.php,每天一个WordPress文件:index.php
  10. mongodb update操作
  11. php v9 用于静态页查询登陆状态以及用户信息的ajax接口,phpcms V9如何判断用户是否登录以及登陆后的标签写法问题 - 小众知识...
  12. 【分享】小米MIUI免root一键删除系统内置软件
  13. vue项目添加emoji表情
  14. linux 培训感谢信,应用文(考试)应用文(考).doc
  15. centos7 安装gitea使用
  16. 《慢慢来,一切都来得及》语录
  17. 蓝桥杯星期一(翻日历)
  18. 卷积神经网络膨胀卷积
  19. 灵机一栋团队小黄衫展示
  20. FX5u控制4个伺服,一个完整的项目 程序用 标签分层,说明了定位控制中的公共参数设定、回原点、

热门文章

  1. 炸裂!MySQL 82 张图带你飞
  2. 打怪升级之小白的大数据之旅(五十九)<Hadoop优化方案>
  3. 如何设计文化衫中考计算机试题,浙江省杭州市实验外国语学校2018-2019学年下新中考科学模拟试题卷(二)...
  4. 近红外二区荧光AgAuSe合金化量子点,第二近红外窗口(NIR-II,900-1700 nm)荧光量子点
  5. 等号(=)伪指令、EQU伪指令、TEXTEQU伪指令之间的区别
  6. 干货丨 艾建松:深瞳云涂大数据支撑户外媒体投放策略及效果监测
  7. 习题 6.11 输出以下图案:*****
  8. [js点滴]JavaScript之文档事件08
  9. matlab怎么做参数估计,[转载]参数估计(matlab)
  10. 解决微信支付申请扫描提示“操作超时,请重新扫码确认问题”