深入理解JVM(二):垃圾回收篇

本文参照尚硅谷-宋红康老师的视屏进行整理,仅供大家学习使用,如果侵权,请联系

上篇:https://blog.csdn.net/u010323860/article/details/107782859

1、垃圾回收相关简单介绍

     (1)什么是垃圾?在运行程序当中没有任何指针指向的对象。

     (2)为什么需要GC?如果不进行垃圾回收,内存迟早会消耗完。

     (3)JAVA有自动内存管理机制,也就是自动分配内存和回收,降低了内存泄漏和内存溢出的风险。

     (4)System.gc(),显示调用,会触发Full Gc,该方法也无法保证一定会调用垃圾回收器进行回收

     (5)内存泄漏和内存溢出:

            内存溢出就是OOM;为新对象分配内存时,没有空闲内存,并且垃圾回收器也无法提供更多内存

            内存泄漏:对象不再被程序用到了,但是垃圾回收器又回收不了{HotSpot虚拟机中可能存在的情况就是对象不使用了,但                                是引用忘记断开了,也就是GCRoots还是可达的,但是确实不使用了;比如单例模式,它的声明周期跟应用                                  程序一样长,如果单例模式关联了某个对象,就可能造成内存泄漏;一些外部资源,如IO没有close}。

     (6)并行是一个时间点上多个程序在执行(只有多个CPU、或者一个CPU多核才可能发生并行);

         并发是一段时间内多个程序在执行,一个时间点一定只有一个程序在执行【只不过CPU切换效率比较高】

         在垃圾回收器中:并行(parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

                                   并发(Concurrent):指用户线程与垃圾收集线程同时执行(可能并行;也不一定是并行的,可能会交替                                                                           进行),用户程序在继续镜像,而垃圾收集程序运行于另一个CPU上。

      (7)强引用、软引用、弱引用、虚引用

           强:如果这个对象是可达的,即使包OOM,这类对象也不会被回收{程序中百分之九十都是强引用 Object a = new                                Object()}

           软:java.lang.ref.SoftReference表示,当内存不足时会回收这类对象{即使可达}

           弱:java.lang.ref.WeakReference表示,当JVM垃圾回收时,无论内存是否充足,此类对象都被回收

           虚:java.lang.ref.PhantomReference表示,在任何时候都可能被垃圾回收器回收。

2、垃圾回收相关算法

     2.1 标记阶段{标记对象是垃圾的过程}

          (1) 引用计数法【JAVA中没有使用这个算法】

                 原理:对每个对象都保存一个整型的引用计数器属性,用于记录对象被引用的情况。引用加1,失效减1,当为0的时                                候回收

                 优点:实现简单,判定效率高

                 缺点:无法处理循环引用的问题

                 Python中使用了这个算法,它是通过手动解除{在合适的时候解除引用关系}、弱引用{使用weakref}的方式解决循环引                     用的问题

          (2) 可达性分析算法  

                 原理:可达性分析算法就是以根对象(GC Roots)集合为起始点,按照从上到下的方式搜索根对象到所连接的对象是否                              可达

                 可作为GC Roots的:

                         (1)虚拟机栈中引用的对象{各个线程被调用的方法中使用到的参数、局部变量}

                         (2)本地方法栈内的对象{Native方法}

                         (3)类的静态属性引用的对象、字符串常量池里的引用[方法区JDK1.7之前]

                         (4)同步锁的对象【以上四种记忆的小技巧就是堆周边的运行时数据区都可能作为GC Roots】

                         (5)还有一种特殊情况,就是当只对新生代进行垃圾回收时,老年代中还可能存在对新生代的引用,老年代也可做                               为GC Roots【也就是分代收集】

                  对象的finalization机制:

                           调用时机:当垃圾回收器发现一个对象没有被引用时,总会先调用对象的finalize方法(对象存活一生中只调一次)

                           finalize()是object类中的方法,允许子类覆写,尽量不要主动的去掉该方法

                           由于该方法的存在,对象可能存在三种状态

                                 (1)可触及的:也就是GC Roots可达,是被引用的

                                 (2)可复活的:就是第一次标记GC Roots不可达,但是调用finalize()时复活了

                                 (3)不可触及的:不可能在被引用了,不可能被复活,一定会被回收

                   判断对象是否可回收过程:

                           (1)如果该对象到GC Roots没有引用链,进行第一次标记

                           (2)判断对象是否有必要执行finalize方法(子类覆写了才执行,因为父类中是空的),如果没有必要执行,则直接                                   标记该对象不可触及;如果需要执行finalize方法,则该对象会插入到F-Queue队列中,等待低优先级线程                                     Finalizer去触发finalize方法,如果finalize方法中该对象到GC Roots又有引用链了,则该对象变为可触及,否                                 则变为不可触及

                    JProfiler工具       

          2.2 清除阶段

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

                      过程:会停止整个程序Stop The Word,然后进行两项工作标记-清除;标记是从GC Roots开始遍历,标记所有引                                     用的对象,也就是可达对象,然后将这个标志位放在对象的Header中;清除是对堆内存从头到尾进行线性                                     遍历所有对象,如果某个对象的Header中没有被标记为可达对象,那么就将它回收

                      优点:基础、常见

                      缺点:效率不算高(两次堆内存对象全遍历),进行GC的时候需要停止整个应用程序,用户体验差,容易产生内存碎                                   片,内存空间不连续{需要维护一个空闲列表}

                (2) 复制算法

                      过程:将内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用                                   的的内存块中,之后清除正在使用的内存块中的所有对象,然后交换两个内存的角色 {survivor0、survivor1}

                      优点:没有标记-清除过程,实现简单,运行高效;不会出现内存碎片,内存是连续的{再次分配内存用的指针碰撞}

                      缺点:每次只有一半的内存空间可用;对象迁移后,其它地方的引用还需要变换内存地址,STW(Stop the word)

                      注意:此算法只适用垃圾对象比较少,而且朝生夕死的那种,适用新生代。【如果对象多,存活时间长,相当于都                                   是可达的,平白无故的复制了迁移了一次】 

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

                       过程:标记是从GC Roots开始遍历,标记所有引用的对象,也就是可达对象,然后将这个标志位放在对象的                                            Header中;第二步是将所有存活对象压缩到内存的一端,按顺序排放,清除边界外所有空间

                       优点:整理了内存碎片,内存更连续(指针碰撞分配新内存);内存也不用减半了

                       缺点:效率上要低于复制算法;移动对象了,需要该引用地址;STW(Stop the word)

                (4) 以上三种算法比对

                      没有最优的算法,只有最合适的算法。所以有分代收集算法,也就是不同的生命周期的对象可以采用不同的收集算                        法,也就是不同的代(新时代、老年代)使用不同的算法【新生代-复制算法;老年代-标记清除或者标记整理】

                       

                (5) 增量收集算法

                      为了解决STW(Stop the word)的时间,体验差的问题,该算法是让垃圾线程和用户线程交替执行,每次垃圾线程只                        收集一小片区域的内存空间,就切换到用户线程,依次反复

                      缺点:来回切换线程,造成额外的消耗,回收垃圾的总成本上升,系统吞吐量下降

                (6) 分区算法

                      为了解决STW(Stop the word)的时间,体验差的问题,该算法是将一块大的内存区域分割成多个小块{region},根                          据目标停顿时间,每次合理的回收若干个小块区间

3、垃圾回收器

    3.1 性能指标

          吞吐量:运行用户代码所占的时间占总运行时间{用户代码执行时间+垃圾收集时间}的比例

          暂停时间:执行垃圾收集时,程序的工作线程被占用的时间{这个指标日益重要}

          内存占用:Java堆内存的大小

          现在GC的基本标准:在最大吞吐量优先的情况下,降低暂停时间。

     3.2 GC回收器

           分类:新生代-----Serial、ParNew、Parallel Scavenge

                     老年代-----Serial Old、Parallel Old、CMS

                     整堆回收器:G1

           查看参数:-XX:PrintCommandLineFlags[+代表使用,-代表不使用]

           Serial回收器(串行回收):

                    原理:采用复制算法+串行回收+STW【新生代用这个算法, 默认老年代用Serial Old,这两个关联】

                    优点:单线程下简单而高效【限定单个CPU,单核,没有线程间切换】

                    关联回收器Serial Old回收器:与Serial回收器类似,不过这个是老年代的回收器,采用标记-整理算法。【不能使                                            用参数设置】

                    参数:-XX:UseSerialGC  指定使用Serial回收器,表明新生代用Serial回收器,老年代用Serial Old回收器

                    收集过程图示:

            ParNew回收器(并行回收,多CPU多核)【除了是多线程进行收集外,与Serial并无差别】

                    这个回收器在JDK9以后就不建议使用了

                    原理:采用复制算法+并行回收+STW【新生代用这个算法, JDK9以前默认老年代用Serial Old,也可以使用CMS;                                JDK14以后CMS回收器被移除了,所以这个回收器基本不用了在最新的JDK上】

                    优点:多核下新生代的收集会更快速,多个线程并行收集,STW时间短了

                    关联回收器:Serial Old回收器、CMS收集

                    参数:-XX:UseParNewGC  指定新生代使用ParNew回收器;-XX:ParallelGCThreads指定并行线程数{一般小于等于                                CPU总核数}

                    收集过程图示:

              Parallel回收器:吞吐量优先,并行,采用复制算法+并行+STW【JDK8的默认回收器】

                       原理:高效利用CPU时间,快速完成程序,老年代会默认采用Parallel Old回收器

                       关联回收器 Parallel Old回收器,采用并行+标记-压缩+STW+高吞吐量    

                       参数:-XX:UseParallelGC,代表新生代是Paralel;

                                 -XX:UseParallelOldGC 老年代采用Parallel Old;【二者可以相互激活】

                                 -XX:ParallelGCThreads指定并行线程数{一般小于等于CPU总核数}

                       收集过程图示:

               CMS回收器{低延迟}Concurrent Mark Sweep,老年代回收器【JDK9标记弃用,JDK14之后该回收器已经被干掉了】

                        是HotSpot虚拟机中第一个真正意义上的并发收集器,垃圾线程、用户线程可以同时工作

                       原理:初始标记-会出现STW,但时间比较短,主要任务是只标记出GC Roots能直接关联到的对象

                                  并发标记-从GC Roots的直接关联对象{初始标记的那些对象}开始遍历整个对象图的过程,耗时较长但不需                                                    要暂停用户线程

                                  重新标记-由于在并发标记阶段,用户线程也在运行,所以这个阶段是修改并发标记期间,因用户程序继续                                                    运作而导致标记产生变动的那一部分对象的标记记录{当然这些对象还是第一步那些对象,不会对                                                    新产生的对象进行标记}STW

                                  并发清除-清除删除掉标记阶段判断的已经死亡的对象,释放内存空间{并发,用户线程和清除线程同时运                                                        行,因为是标记清除算法,不需要移动对象}

                       优点:并发收集,并发清除,低延迟。

                       缺点:会产生内存碎片;对CPU资源敏感,并发阶段,会抢占CPU,造成吞吐量降低;无法处理浮动垃圾,浮动                                      垃圾就是在并发标记阶段,用户线程所产生的新垃圾;{在并发标记阶段,如果内存不够用的话,会报一次                                    Concurrent  Mode Failure,然后开启备用收集器Serial Old收集器开始单线程收集老年代};

                       参数:-XX:UseConcMarkSweepGC,手动指定使用CMS垃圾回收器,会自动加UsePaNewGC打开

                                  -XX:CMSlnitiatingOccupanyFraction 设置堆内存使用率,也就是阈值,到了这个值就开始触发CMS,因为                                           在CMS存在并发标记阶段,不可能等内存要满了才回收,否则在并发标记阶段可能就会内存溢出,所                                             以设置这个值{JDK6以后的版本默认是92%}

                                  -XX:UseCMSCompactAtFullCollection  指定在执行完Full GC后对内存进行压缩整理{STW}

                                  -XX:CMSFullGCsBeforeCompaction 指定多少次Full GC后对内存进行压缩整理

                       收集过程图示:   

             G1回收器:区域分代化,region,  在延迟可控的情况下获得尽可能高的吞吐量。【JDK9以后默认的回收器】

                      侧重点在于优先回收垃圾最大量的区间,所以称为Gaarbage First。

                       Region:每个region内部是通过指针碰撞进行内存分配的,通过TLAB进行控制。

                       RememberedSet:一个region中的对象可能被其他任意region中对象引用,判断对象是否存活时,是否需要扫描                                                         整个堆的region一个个判断是否引用该对象吗?

                                                  每个region都有一个对应的RememberedSet,这里记录了引用这个区域的其他region{除它自己}

                       原理:当年轻代的Eden区用尽时开始年轻代的回收过程;当堆内存使用达到一定值(默认是45%),开始老年代的                                      标记过程,标记完成后开始混合回收过程,G1的老年代回收器是一次只需扫描/回收一部分老年代的region

                                  初始标记-这个阶段是STW的,所有应用线程会被暂停,标记出从GC Root开始直接可达的对象。

                                  并发标记-从GC Roots开始对堆中对象进行可达性分析,找出存活对象,耗时较长。若发现某个region                                                           中的所有对象都是垃圾,那个这个区域会被立即回收。

                                  最终标记-标记那些在并发标记阶段发生变化的对象,将被回收

                                  筛选回收-首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回                                                   收一部分Region{STW}

                       优点:并行与并发分代收集,它将堆分成若干个eden、survivor、old区,兼顾整个堆的垃圾收集;空间整合,                                     G1将堆分成一个一个的region,region之间采用赋值算法,整体上采用的是标记-整理算法,无碎片化;可                                     预测的停顿时间,可以指定STW时间,G1也会尽可能的满足在收集垃圾上停顿的时间不大于指定的值,是                                     因为G1可以只收集部分region,每次根据允许的收集时间,优先回收垃圾占比高的region区域;

                       缺点: 会多占用内存{rset}【内存大于8G时G1的效率比较高】

                       参数:-XX:+UseG1GC,使用G1回收器,JDK9之后是默认。

                                 -XX:+G1HeapRegionSize,设置每个Region的大小,值是2的次幂,范围是1-32MB。

                                 -XX:+MaxGCPauseMillis,设置最大的GC停顿指标,默认是200ms【并不是越小越好】            

    3.3 总结 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值