垃圾回收机制

  java的垃圾回收机制(gc)是java的一大特点,它不像c语言一样需要程序员去手动回收。它是由jvm单独启动一个系统级的线程去进行gc活动的。

  以下将涉及到垃圾回收机制的判定标准、判定算法、回收算法 回收器

  

  判定标准

    如果某个对象没有被引用,则认为这个这个对象是垃圾对象 ,需要被回收。

  

  标记算法

    判定的算法目前由两种,引用计数器算法和可达分析算法。

    引用计数算法:这个方法就是将对象中添加一个引用计数器,每当有一个地方回去引用这个对象时,计数器就会加1,当引用失效时,计数器就会减1,当计数器等于0时,就表示这个对象未垃圾对象,可以被回收

           缺点:无法检测出循环引用的情况,容易导致内存泄露

           优点:执行效率高,程序影响较小 

    可达分析算法:它是通过一系列的“GC ROOTS”对象作为起点,向下搜索,如果某个对象从起点到这个对象不存在可达路径(引用链),则表明这个是垃圾对象,需要回收,如果存在,则表明该对象还是可用对象。

   

  回收算法:

    标记-清除算法:会先针对对象进行是否是垃圾对象的标记,标记后,就是对这些对象进行清除。

        缺点:容易造成大量的不连续的内存碎片,效率不高

    

    复制算法:该算法是将内存分为两个相等的区域,每次只使用其中一块,当其中一块内存快满的时候,会将这块的内存中存活的对象顺序复制到另外一块内存区域中,以及清理掉当前这块的内存区域

        优点:解决碎片化问题,顺序分配内存简单高效

        缺点:只适用于存活率低的场景,如果存活率高了,就会浪费接近一半的内存区域

 

    标记-整理算法:这个算法是为了解决复制算法里面浪费内存的问题提出的解决方案,该算法的标记阶段和标记清除的算法的标记阶段类似,在标记后,它是将可存活的对象移动到内存的一端,然后将边界以外的另外一端的对象清除掉。

    

    分代回收算法:分代回收算法中有年轻代、老年代、永久代的概念

           分代回收算法通过年轻代、老年代和永久代的顺序去说明,

           年轻代:年轻代的内存区域被分为一个eden区域和两个survivor区域,当一个对象创建初始会放入到eden区域,然后在经过一次gc后,会将eden区域存活的对象和一个survivor区存活的对象移动到  

               另外一个survivor区域,当一个对象在经过一定次数(当前默认15次)的gc后还存活,会将这个对象移动到老年区。由于年轻代里会回收大量的对象,所以一般采用的是复制算法。

           老年代:老年代则是存放那些年轻代转移过来的存活比较久的对象,或者那些占用内存比较大的对象会直接放入到老年代,这里一般采用的是标记清除或者标记整理。存在性能问题、内存溢出问                            题。

           永久代:永久代主要是放类的class信息和meta(元数据信息),(jdk1.8 元空间替换了永久代)解决了容易内存溢出的问题,

               元空间和永久代的主要区别是:永久代将class对象 和meta信息是放在jvm分配的内存中,受jvm限制,而元空间主要是放在服务器的内存中的,受限于服务器内存。

 

 

  垃圾收集器:serial收集器、ParNew收集器、Parallel Scavenge收集器、serial old收集器、Parallel old收集器、CMS收集器、G1收集器

     serial收集器:最基本的最悠久的收集器,单线程、简单高效、运用于新生代,采用的算法是复制算法,

           缺点:在回收器回收时,需要暂停其他的所有线程工作,直到回收完成,

           适用场景:单线程、单cpu的client模式的虚拟机

     ParNew收集器:是基于serial收集器的多线程版本,采用复制算法,

           特点:ParNew默认开启的线程的数量和cpu数量相同,在cpu非常多的情况可以通过设置-XX:ParallelGCThreads参数来修改,

           缺点:同serial收集器一样。

           适用场景:它是运行在server模式下的首选新生代的回收器,因为它是除了serial收集器外,唯一能与cms收集器工作的新生代收集器。

    Paraller Scavenge收集器:注重吞吐量的多线程收集器,目标是使得系统的吞吐量达到可控制的目的,采用的也是复制算法;与ParNew的另外一个区别是Paraller Scavenge的GC自适应调节策略。

                Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年                               代的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为GC的自适应调节策略。

                Parallel Scavenge收集器使用两个参数控制吞吐量:

                   XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间

                   XX:GCRatio 直接设置吞吐量的大小。

 

    Serial old收集器:是Serial的老年代版本的收集器。特点也是适合单线程服务,采用的是标记-整理算法

           主要应用场景:单线程的client模式,也可以在serve模式下运用(在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用。作为CMS收集器的后备方案,在并发收集Concurent Mode                        Failure时使用。

 

     Parallel Old收集器:是Parallel Scavenge收集器的老年代版本。

            特点:多线程,采用标记-整理算法。

            应用场景:注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收集器。

 

 

    CMS收集器:注重响应速度的基于标记-清除的收集器,应用在老年区,

            处理步骤:

                初始标记:只是标记一下GC Roots能直接关联到的对象,速度很快

                并发标记:对初始标记标记过的对象,进行 trace(进行追踪,得到所有关联的对象,进行标记)

                重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。

                并发清除:清除美欧被标记的对象

 

            缺点:

                会发生stop the wolrd(全局停顿) 

                产生内存碎片(产生大对象时,容易引发full gc)

                对cpu资源敏感(并发时会和和用户线程一起抢占cpu)

                在并发标记产生的浮动垃圾需要等到下一次gc才能清理。  

 

    G1收集器:一款面向服务端应用的垃圾收集器。为了实现STW的时间可预测,首先要有一个思想上的改变。G1将堆内存“化整为零”,将堆内存划分成多个大小相等独立区域(Region),每一个Region都可以根             据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的                                     旧对象都能获取很好的收集效果。

           Region可能是Eden,也有可能是Survivor,也有可能是Old,另外Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对             象。每个Region的大小可以通过参数-XX:G1HeapRegionSize设定,取值范围为1MB~32MB,且应为2的N次幂。而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous           Region之中,G1的进行回收大多数情况下都把Humongous Region作为老年代的一部分来进行看待。

         G1在逻辑上还是划分Eden、Survivor、OLd,但是物理上他们不是连续的。

          

        

        G1的运行过程与CMS大体一致,分为以下四个步骤:

        初始标记(Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线             程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
        

                             并发标记( Concurrent Marking):从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,并             发时有引用变动的对象会产生漏标问题,G1中会使用SATB(snapshot-at-the-beginning)算法来解决,后面会详细介绍。
        
                             最终标记(Final Marking):对用户线程做一个短暂的暂停,用于处理并发标记阶段仍遗留下来的最后那少量的SATB记录(漏标对象)。
                             

                             筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构                                    成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多个收集器线程并行完成的。

                            TAMS是什么?要达到GC与用户线程并发运行,必须要解决回收过程中新对象的分配,所以G1为每一个Region区域设计了两个名为TAMS(Top at Mark Start)的指针,从Region区域划出一部分空间用于记录并发回收过程中的新对象。这样的对象认为它们是存活的,不纳入垃圾回收范围。

 

        参考链接:(20条消息) G1垃圾收集器详解_morris-CSDN博客_g1收集器

        参考链接:Jvm垃圾回收器(终结篇) - 不二尘 - 博客园 (cnblogs.com)

posted @ 2021-11-29 19:14  whltw  阅读(92)  评论(0)    收藏  举报