JVM 垃圾回收

前言:

垃圾回收(Garbage Collection)是JVM中非常重要的一个环节,垃圾回收的效率直接影响了程序的运行和内存空间,下文将会对垃圾回收算法、垃圾收集器等做一个详细介绍。

正文:

垃圾标记阶段算法:

        在垃圾回收过程中会存在一个垃圾标记的阶段,在此阶段相应的标记算法会将所有的不会被回收的对象进行一个标记,最常见的标记算法有两个:引用计数算法和可达性分析算法。而在Hotspot中使用的是可达性分析算法

引用计数算法是在对象中设置一个计数器,用于对本对象被引用的次数进行一个计数,类似于分代年龄计数器,多被一个对象指向计数器便+1,具体如图:

由于这种算法是根据计数器的数值为依据来进行回收的,反应灵敏算法的效率较高,但是可以仔细看到图中如果变量(var)对对象的引用取消了之后,三个对象便成了循环引用的情况,而这种情况下,这三个对象是对于程序没用的,但是JVM又无法回收因此会造成内存泄漏(Momery Leak),所以这种缺点是致命的。

        可达性分析算法用途更加广泛,GCRoots被作为垃圾回收的根节点,通过对引用树的遍历将所有可达的对象在对象头中进行一个标记。这种算法不会有循环引用的问题发生!

/*
  作为GCRoots的对象主要有四个(还有一些不细讲)
      1.虚拟机栈中引用的对象
      2.本地方法栈引用的对象
      3.类静态属性引用的对象
      4.方法区常量引用的对象
*/

垃圾回收算法:

        标记-清除算法:

                该算法有两个步骤:①标记 ②清除

                ①:在标记阶段,第一次遍历主要是利用上文讲的可达性分析算法对可达的对象进行标记,在对象头中进行相关的设置 ②:第二次遍历所有的对象,对在对象头中没有相关设定的对象进行回收,但并不是直接在内存中删除,而是维护一个列表用来记录相关的地址信息,等下一次真的有对象需要利用到这片空间的时候再进行移除。该算法有一个致命的缺点:会产生内存碎片。内存碎片对于内存是非常不利的。

        复制算法:

                也叫标记-复制算法,第一步也是进行标记,与标记-清除算法一样,对可达的所有对象进行标记。不同点在于该算法需要两倍的空间,用于对对象的复制,例如在幸存者区中有1区和2区,对象就是在这两片空间中进行相互复制。在原先的区域中标记完成之后,将所有标记了的对象规整地复制到另外一个区域,而不会产生内存碎片。完成之后再将原先区域中的“垃圾”全部回收。复制是真正意义上的复制对象,因此对于回收率较高的区域进行回收效率低下。在对象地址改变的时候其他指向它的引用也要发生改变,较麻烦。

        标记-压缩算法:

                标记-压缩算法标记阶段跟上面相同,标记完成之后将所有的不可达对象进行清除,最后再将可达对象进行压缩(整理),变成规整的内存空间。不存在内存碎片的问题,与复制算法相同,对象地址改变,指向它的引用信息也需要更改。

        思想:

                1.分代收集算法:最直观的就是在运行时数据区中,对于堆区、元空间以至于新生代和老年代的垃圾收集算法都不一样。每个区域都有它的特点,因此不能一概而论,具体问题具体分析,不然反而会加大垃圾回收的难度。

                2.增量收集算法:在进行垃圾回收的时候,一定会有STW(stop the world)的处理业务逻辑的暂停阶段,而为了给用户更好的体验,发明者将一整个STW的阶段分为多次STW,使得单次的暂停时间减少,进而提高用户的体验。

                3.分区收集算法:“分区“这个概念在G1收集器中出现的最多,G1也是第一款分区收集的垃圾回收器。顾名思义,分区就是将所有进行垃圾回收的区域看作是一个整体,然后在其中将其划分为一个一个的区(region)对区进行垃圾回收,效率更高。

        内存泄漏:

                内存泄漏(Momery Leak)指的是对象不会再被使用但是却回收不掉的对象。这种对象占用内存空间,严重的可能会发生OOM(内存溢出)。在JVM中最常见的内存泄漏是单例模式下,对象引用了外部的对象而且外部的对象不被使用,数据库、IO的资源未关闭也会发生泄漏。

        安全点和安全区:

                垃圾回收并不是在任何时候都可以进行的,程序必须运行到安全点或者安全区才可以进行回收。通常在程序中设置一个静态变量来判断安全点,JVM会自动判断该位置是否为安全点,true则进行回收。但是如果线程正在sleep()不能进行接下来的操作,则需要安全区的概念,安全区是特殊的安全点,范围更大,程序运行到安全区中便可以回收。

垃圾收集器:

        六大经典垃圾收集器:

                

 左边六个为经典的垃圾回收器,上面三个是新生代的回收器,下面三个是老年代的回收器。

Serial GC和Serial Old GC:串行垃圾回收器,在单核的cpu中能够发挥很高的效能。使用复制算法,通常与Serial Old GC一起搭配使用,使用标记-压缩算法,同时Serial Old GC 被作为CMS不能正常使用时的一个后备回收器。

ParNew GC和CMS:ParNew 是并行的垃圾回收器,使用复制算法在同一段时间内有多个回收线程可以进行回收,比Serial GC效率更高。与之搭配的是CMS:第一款并发的低延迟的垃圾回收器。利用标记-清除算法,CMS回收的阶段分为初始标记->并发标记->重新标记->并发清除->重置线程。其中需要关注的是并发标记阶段,与其他标记阶段不同,在并发标记阶段垃圾回收线程和用户线程可以交替运行,大大加快了垃圾回收的效率。缩短了STW的时间。

Parallel Scavenge GC和Parallel Old GC:吞吐量优先的垃圾回收器(与ParNew不同),总体的用户线程的运行时间更高,程序执行效率更高。Parallel Scavenge GC使用的也是复制算法。与Parallel Old GC搭配使用(标记-压缩算法)。

G1垃圾回收器:在延迟可控的情况下,获得尽可能高的内存吞吐量。作用于新生代和老年代。

G1作为新款的垃圾回收器具有里程碑式的意义,它是第一款分区回收的垃圾回收器,G1将所有要进行GC的区域划分为一个一个的区域(region)。通过可预测的停顿时间模型,根据用户设定的延迟时间,在设置的延迟时间内回收价值量较高(回收率高)的区域,效率很高。

        基本步骤:YGC->YGC+老年代标记->Mixed GC   可选:->Full GC

第一步进行新生代的垃圾回收,之后同时进行老年代对象的标记,标记完成之后直接进行Mixed GC对全部新生代和一部分的老年代进行回收。

                

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值