首页 > 编程知识 正文

垃圾回收的意义,jvmcms过程

时间:2023-05-05 16:28:03 阅读:14936 作者:4192

1.1 G1采集器的设计目标首先介绍两个概念:吞吐量和响应能力、响应能力和吞吐量是评价一个系统的两个重要指标

吞吐量关注在指定的时间内最大化APP应用程序的工作量。

如下测量系统吞吐量的好坏。

同一事务(或任务、请求)在一定时间内完成的次数(tps ) )。

在一定时间内完成几次查询

在关注吞吐量的系统中,一定次数的纸箱,即stw是可以接受的。 该系统关注长时间大量任务的执行能力,因此一次快速响应不值得考虑

响应能力是指程序或系统是否能够及时响应请求。 例如:

桌面用户界面能多快响应事件

网站能多快返回页面请求?

数据库可以多快返回查询中的数据

在这种对响应能力敏感的场景中,不能接受长时间的停顿,而是关注每个反应的能力。

g1收集器是面向服务器的垃圾收集器,适用于多核处理器,是具有大内存的服务端系统。

在满足短时间的gc停止的同时,达到高吞吐量。

适用JDK7以上版本

与APP应用程序线程同时运行,几乎不需要停止工作(类似于CMS );

G1的设计计划是替换CMS。 G1在几个方面弥补了CMS的不足。 例如,CMS使用fqdsy-sweep算法,自然会产生内存碎片。 (CMS为全GC时,只能在stop the world中整理内存碎片),而G1基于copying算法,不需要管理内存,有效地整理剩下的内存

GC姿势更可控,也可以设定时间阈值。 例如,不是像CMS那样必须回收所有旧的年代,而是可以回收某一部分的旧的年代(其他收集器的时间可能难以掌握)

1.2 G1重要概念1.2.1分区(Region ) G1采用了不同的策略来解决并行、串行、CMS收集器碎片、暂停时间不可控等问题。 G1将整个堆称为相同大小的分区或区域) Region )

G1的堆结构如下。

每个分区可能有年轻一代也可能有老一代,但在同一时刻只属于某一代。 年轻一代、survivor区、古老年代的概念还存在,已成为逻辑概念。 这样,很容易重用以前分代框架的逻辑。 如果分区不需要物理连续,则可以在老年代上只回收部分区域(Region ),这是额外的好处。wxdym代仍然是在wxdym世代满时,回收整个wxdym世代对于每个wxdym世代中的对象,要么被回收,要么被提升。 之所以对wxdym世代也采用分区机制,是因为这样容易与老年代的战略统一,调整世代的尺寸【其实wxdym世代不适合这样的垃圾】,也可以不选择像老一代那样回收利用效果好的Region】

G1也有一个特殊的区域叫Humongous区域。 如果一个对象占用的空间超过分区容量的50%,或者占用的空间超过分区容量的50%,则G1收集器会将其视为巨大对象。 默认情况下,这些巨大对象直接指定给较早的年代,但如果这些对象是短期存在的巨大对象,则会对垃圾收集器产生负面影响。 为了解决这个问题,G1划分了用于存储巨大对象的Humongous区域。 如果一个h区域容纳不下一个巨大对象,G1将查找并保存连续的h分区。 为了找到连续的h区,可能需要启动全GC。

1.2.2收集集合(CSet )收集集合(Collection Set )一组可重用分区。 CSet上存活的数据在GC过程中被移动到另一个可用分区,并且CSet中的分区可以从eden空间、survivor空间或老分区中获取,并且可以同时包括这些分区中的每一代

1.2.3已存储集合(RSet )已存储集合(Remember Set ):RSet允许区域Region并行和独立,RSet记录其他Region中的对象引用本Region中的对象的关系,points RSet的价值在于,垃圾收集器不需要扫描整个堆以找到引用当前分区中对象的人,而只需扫描RSet即可。

点对点重置分析G1 GC通过向点对点的卡片表中添加更多结构来配置点对点重置。 每个Region都使用点对点重置记录来记录哪个另一个Region有自己的指针,以及这些指针分别位于哪个caion上,该重置实际上是hash table,而key是单独的

例如,如果region A的Rset中项目的密钥为region B,value中有索引为1234的card,则表示region B的card中有对region的引用

A。所以对 region A来说该RSet记录的是 points-into的关系,而 card table仍然记录了 points-out的关系。

car table 解析

        card table是什么又是怎么来的呢?如果一个对象引用的对象很多,赋值器需要对每个引用做处理,赋值器开销会很大,为了解决赋值器开销这个问题,在G1中又引入了另外一个概念,卡表( Card Table)。一个 Card Table将一个分区在逻辑上划分为固定大小的连续区域每个区域称之为卡。卡通常较小,介于128到512字节之间。 Card Table通常为字节数组,由Card的索引(即数组下标)来标识每个分区的空间地址。

下图表示了RSet、Card和Region的关系):

 

        上图中有三个Region,每个Region被分成了多个Card,在不同Region中的Card会相互引用,Region1中的Card中的对象引用了Region2中的Card中的对象,蓝色实线表示的就是points-out的关系,而在Region2的RSet中,记录了Region1的Card,即红色虚线表示的关系,这就是points- into。  

RSet究竟是怎么辅助GC的呢?

        在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。

1.3 GC模式

G1提供了两种GC模式, Young GC和 Mixed Gc两种都是完全 Stop The World的

1.3.1 Young GC

        Young GC:将选择所有年轻代里的 Region 进行GC(所以CSet里存的就是所有年轻代里面的Region)。将存活的对象复制到幸survivor区是多线程执行的并且需要stw,如果满足晋升到老年代阈值,则某些对象将被提升到老年代,并通过动态计算出下次GC时堆中年轻代的 Region个数,即年轻代内存大小来控制 Young GC的时间开销。

        Young GC的触发时机

        Young GC在欣慰的大山充满时触发,在回收之后所有之前属于欣慰的大山的区块全部变成空白即不属于任何一个分区(欣慰的大山、 Survivor、Old)

1.3.2 Mixed GC

Mixed GC:将选择所有年轻代里的 Region,外加根据 global concurrent fqdsying统计得出收集收益高的若干老年代 Region进行GC(所以CSet里存的是所有年轻代里的 Region加上在全局并发标记阶段标记出来的收益高的 Region)。在用户指定的开销目标范围内尽可能选择收益高的老年代 Region,以此来控制Mixed GC的时间开销。

需要注意的是:Mixed GC不是 Full GC,它只能回收部分老年代的 Region而不是全部的Region,如果 Mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行 Mixed GC,就会退化然后使用 serial old GC( Full GC)来收集整个 GC heap。所以本质上,G1是不提供 Full GC的。所以总的来说,G1的运行过程是这样的:会在 Young GC和Mixed GC之间不断地切换运行,同时定期地做全局并发标记,在实在赶不上对象创建速度的情况下使用Full Gc( Serial GC)。

它的GC步骤分为两步全局并发标记(global concurrent fqdsying)和拷贝存活对象(evacuation)

Mixed gc 中的Global concurrent fqdsying

Mixed gc 中的 global concurrent fqdsying的执行过程类似于CMS,但是不同的是,在G1 GC中,它主要是为 Mixed GC提供标记服务的【老年代之所以知道为什么哪里的回收效益高就是根据此服务得出的结果确定的】,并不是一次GC过程的一个必须环节。global concurrent fqdsying的执行过程分为四个步骤

初始标记( initial fqdsy,STW):它标记了从GC Root开始直接可达的对象。第一阶段 initial fqdsy是共用了 Young GC的暂停,这是因为他们可以复用 root scan操作,所以可以说 global concurrent fqdsying是伴随 Young GC而发生的。

并发标记( Concurrent Marking):这个阶段从GC Root开始对heap中的对象进行标记,标记线程与应用程序线程并发执行,并且收集各个Region的存活对象信息。

重新标记( Refqdsy,STW) :标记那些在并发标记阶段发生变化的对象,将被回收。

清理( Cleanup ):清除空 Region(没有存活对象的),加入到 free list。第四阶段 Cleanup只是回收了没有存活对象的 Region,所以它并不需要STW

1.3.3 G1的收集概览

        G1算法将堆划分为若干个区域( Region),它仍然属于分代收集器。不过,这些区域的一部分包含wxdym代和老年代,wxdym代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有CMS内存碎片问题的存在

        YGC收集时如何找到根对象呢?老年代所有的对象都是根对象吗?那如果直接对整个老年代进行扫描的话会耗费大量的时间。于是G1引入了我们上面提到的RSet的概念,作用是跟踪指向某个heap区内的对象引用。但是在wxdym代之间记录引用吗?这是不必要的,原因在于每次GC时所有wxdym代都会被扫描,所以只需要记录老年代到wxdym代之间的引用即可。YGC收集过程如下:

三色标记算法

        这是Mixed gc 中的Global concurrent fqdsying中的内容,提到并发标记,我们不得不了解并发标记的三色标记算法,fqdsy的过程就是遍历heap标记 live object的过程。它是描述追踪式回收器的一种有效的方法,利用它可以推演回收器的正确性。我们将对象分成三种类型:

黑色:根对象或者该对象与它的子对象都被扫描过(即对象被标记了,且它的所有 field 也被标记完了)

灰色:对象本身被扫描,但还没扫描完该对象中的子对象(即它的 field 还没有被标记或标记完)

白色:未被扫描对象,如果扫描完成所有对象之后最终为白色的为不可达对象,即垃圾对象(即对象没有被标记到)

 

 

 

 

可能出现的问题:

 

这时候应用程序执行了以下操作A.c=C和B.c=null这样,对象的状态图变成如下情形

 

这时候垃圾收集器再去标记扫描的时候就会变成下图这样,这样就会造成漏标:

 

 这样C本来应该是存活对象,但是由于A对象已经扫描完成,造成C对象的错误删除。

解决方法-SATB

SATB snapshot at the beginning

在开始的时候生成一个快照图,标记存活的对象

在并发标记的时候所有被改变的对象入队(在write barrier里把所有旧的引用所指向的对象都变成非白的,如上图中,将B所指向的引用C变成非白的)

可能存在浮动垃圾,将在下次垃圾收集时被收集

SATB详解

SATB是维持并发GC的一种手段。G1并发的基础就是SATB。SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对象就认为是活的,从而形成个对象图。在GC收集的时候,wxdym代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象

如何找到在GC过程中分配的对象呢? 这是收集过程中可能遇到的第二个问题,每个Region记录着两个top-at-fqdsy- start(TAMS)指针,分别为 prevtamst和 next AMS。在TAMS以上的对象就是新分配的,因而被视为隐式 fqdsyed。通过这种方式我们就找到了在GC过程中新分配的对象,并把这些对象认为是活的对象

解决了对象在GC过程中分配的问题,那么在GC过程中引用发生变化的问题怎么解决呢?Write Barrier就是对引用字段进行赋值做额外处理。通过 Write Barrier就可以了解到哪些引用对象发生了什么样的变化。

SATB仅仅对于在 fqdsying开始阶段进行snapshot"(fqdsyed all reachable at fqdsystart)),但是 concurrent的时候并发修改可能造成对象漏标记

对 black新引用了一个 white对象,然后又从gray对象中删除了对该 white对象的引用这样会造成了该whie对象漏标记

对 black新引用了一个 white对象,然后从gray对象删了一个引用该 white对象的 white对象,这样也会造成了该 white对象漏标记

对 black新引用了一个刚new出来的 white对象,没有其他gray对象引用该 White对象这样也会造成了该 white对象漏标记

对于三色算法在 concurrent的时候可能产生的漏标记问题,SATB在 fqdsying阶段中对于从gray对象移除的目标引用对象标记为gray,对于 black引用的新产生的对象标记为 black;由于是在开始的时候进行snapshot,因而可能存在 Floating Garbage

漏标与误标

误标没什么关系,顶多造成浮动垃圾,在下次gc还是可以回收的,但是漏标的后果是致命的把本应该存活的对象给回收了,从而影响的程序的正确性

漏标的情况只会发生在白色对象中,且满足以下任意一个条件

并发标记时,应用线程给一个黑色对象的引用类型字段赋值了该白色对象

并发标记时,应用线程删除所有灰色对象到该白色对象的引用(但是此时或许有黑色对象引用该白色对象)

如何解决以上问题?

对于第一种情况,利用 post-write barrier,记录所有新增的引用关系,然后根据这些引用关系为根重新扫描一遍,Write Barrier就是对引用字段进行赋值做额外处理。通过 Write Barrier就可以了解到哪些引用对象发生了什么样的变化

对于第二种情况,利用pre-write barrier,将所有即将被删除的引用关系的旧引用记录下来,最后以这些旧引用为根重新扫描遍,这样就能扫描标记到那个“白色对象”

 G1收集器对比其他收集器:

G1 VS CMS对比使用fqdsy- sweep的CMS,G1使用的copying算法不会造成内存碎片

对比 Parallel Scavenge(基于 copying)Parallel Old收集器(基于fqdsy- compact-sweep), Parallel会对整个区域做整理导致gc停顿会比较长,而G1只是特定地整理压缩某些Region

G1并非一个实时的收集器,与 parallel Scavenge一样,对gc停顿时间的设置并不绝对生效,只是G1有较高的几率保证不超过设定的gc停顿时间。与之前的gc收集器对比,G1会根据用户设定的gc停顿时间智能评估哪几个 region需要被回收可以满足用户的设定

参考文章:Java Hotspot G1 GC的一些关键技术的代码分享(图)其中的部分图文信息参考连接中文章。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。