JVM垃圾回收相关算法

标记阶段:引用计数算法
  • 对每个对象保存一个整型的引用计数属性,用于记录被引用的情况,引用+1,引用失效-1

  • 优点:实现简单,垃圾对象便于识别;判定效率搞,回收没有延迟性

  • 缺点:

    1. 它需要独立的字段存储计数器,这样的作法增加了存储空间的开销
    2. 每次赋值都需要更新计数器,伴随着加法和减法的操作,增加了时间开销
    3. 引用计数器又一个严重的问题,即无法处理循环引用的情况。这是一条致命缺陷,导致在Java的垃圾回收器中没有使用该算法
  • java使用的不是引用计数算法(Python使用了)

标记阶段:可达性分析算法(根搜所算法,追踪性垃圾收集)
  • 可达性算法是以根对象集合(GC Roots)为起点,按照从上往下的方式搜索被根对象集合所链接的目标对象是否可达
  • 使用可达性分析算法后,内存中存活的对象将被直接或间接的连接着,探索所走过的路径叫做引用链(Reference Chain)
  • 如果目标对象没有被任何引用链连接,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象
  • 在可达性分析算法中,只有能够被对象集合直接或间接连接的对象才是存活的对象。
GC Roots包括以下几类元素
  • 虚拟机栈中引用的对象(方法中的参数,局部变量)
  • 本地方法栈内JNI(通常说的本地方法)引用的对象
  • 方法区中静态属性引用的对象(java类的引用类型静态变量)
  • 方法区中常量引用的对象(字符串常量池)
  • 所有被同步锁synchronized持有的对象
  • Java虚拟机内部的引用(基本数据类型对应的Class对象)
  • 反应Java虚拟机内部情况的JMXBean、JVMT中注册的回调、本地代码缓存等
对象finalization机制

finalize(): 当垃圾回收器指向一个对象,即:垃圾回收此对象之前,总会调用该对象的finalize()方法,该方法允许在子类中进行重写,**用于在对象被回收时进行资源释放。**通常在这个方法中进行一些资源释放和清理工作,比如关闭文件、套接字和数据库连接等。

  • 永远不要主动调用finalize()方法
    1. 可能导致对象复活
    2. 执行时间没有保障,完全由GC线程决定,极端情况些,若不发生GC,则永远不会执行
    3. 可能会影响GC性能
  • 因为finalize()方法 垃圾被分为三种状态
    1. 可触及的:从根节点开始可以到达这个对象
    2. 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活
    3. 不可触及的:对象的finalize()被调用,但是对象并没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为finalize()只能被调用一次
  • 判断一个对象是否可悲回收至少要经历两次标记
    1. 如果对象到GC Roots没有引用链,则进行一次标记
    2. 进行筛选,判断对象有没有必要执行finalize()方法
    (1)如果对象没有重写finalize()方法,或者finalize()方法已被虚拟机调用过了,则被虚拟机视为“没有必要执行”,被对象被认为时不可触及的
    (2)如果对象重写了finalize()方法,且没有被执行,那么该对象会被插入到F-Queue对列中,由虚拟机自动创建、低优先级的Finalizer线程触发其finalize()方法执行
    (3)finalize()方法时对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记。如果该对象在finalize()方法中与引用链中的任意对象建立了联系,那么会在第二次标记中吧该对象从即将回收的集合中移除。之后对象再出现没有被引用的情况finalize()方法不会被再次调用,对象直接变为不可触及的,一个对象的finalize()方法只能被调用一次。
清除阶段:标记-清除算法
  • 标记:Collector从引用根节点开始遍历,标记所有被引用的对象。一般时再对象的Header中记录为可达对象
  • 清除:Collector对堆内存从头到尾进行线性遍历,如果发现某个对象再其Header中没有标记为可达对象,则将其回收。
优点:
  1. 效率不算高
  2. 再进行GC的时候,需要停止整个应用程序,导致用户体验差
  3. 这种方式清理出来的空闲空间不是连续的,产生内存碎片。需要维护一个空闲表
何为清除
  • 这里所谓的清除并不是真的置空,而是把需要清除的对象的地址保存到空闲的地址列表里。下次又新对象需要加载时,判断垃圾的位置空间是狗足够,如果够就存放覆盖。
清除阶段:复制算法
思想

将活着的内存空间分为两块,每次只用其中一块,在垃圾回收时将正在使用的内存中活的对象复制到未被使用的内存块中,之后清除正在使用的内存块,交换两个内存角色,最后完成垃圾回收(young区)

优点
  • 没有标记和清除的过程,实现简单,运行高效
  • 复制过去以后保证内存空间的连续性,不会出现“碎片”问题
缺点
  • 此算法的缺点也是非常明显的,就是需要两倍的内存空间
  • 对于G1这种分拆成大量region的GC,复制而不是移动,意味着GC需要维护region之间对象引用关系,不管时内存占用或者时间开销也不小
特别
  • 如果内存中的垃圾非常多,复制算法需要复制的对象的数量不会太大,或者说非常小
清除阶段:标记-压缩算法
执行过程
  1. 和标记-清除算法一样,从根对象开始标记所有引用对象。
  2. 将所有存活的对象压缩到内存的一段,按顺序排放
  3. 清理边界外的所有空间
优点
  • 消除了标记-清除算法中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要只有一个内存的其实地址即可
  • 消除了复制算法中内存减半的高额代价
缺点
  • 从效率上来说,标记-压缩算法要低于复制算法
  • 移动对象的同时需要同时调整引用地址
  • 移动过程中需要全程暂停用户应用程序。STW
分带收集算法
  • 根据年轻代和老年代不同的特性,使用不同的算法进行垃圾回收。
增量收集算法
思想
  • 如果依次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程进行交替执行。每次,垃圾收集线程只手机一小片区域的内存空间,接着切换到应用程序线程。依次反复,直到垃圾收集完成。
    总体来说,增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法通过对线程冲突的妥善处理,允许垃圾收集线程以分段的方式完成标记、清理、复制工作。
缺点
  • 使用这种方式,由于在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。但是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。
分区算法
思想
  • 一般来说,在相同条件下,堆空间越大,一次GC所需要的时间就越长,有关GC所产生的停顿就越长。为了更好的控制GC所产生的停顿时间,将一块打的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少依次GC所产生的停顿。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值