JVM垃圾回收算法

finalize()

由于垃圾回收期只知道回收new分配的内存,其他内存不知道如何释放,因此定义该方法,其功能假定为:

  • 一旦垃圾回收器准备好释放对象占用的储存空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
  • java语言规范并不能保证该方法一定能及时的调用甚至无法保证被调用,因为垃圾回收动作仅跟内存有关,有可能垃圾回收永远不会进行。
  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于析构
  3. 垃圾回收只与内存有关

finalize()方法并不是普通的清理工作场所,它主要用于清理本地对象(非java代码),但它可以用于对象终结条件的验证,例如所有对象在被垃圾回收前都应该被checkin(一个方法),若某个对象忘记checkin,finalize方法里加入了对checkin的检测,就能找出这个隐藏的问题。

引用的分类:

  1. 强引用:代码中普遍存在的,如Object o = new Object();只要引用存在,就不会被回收
  2. 软引用:当内存不足时才会二次回收,若回收后仍内存不足,抛出内存溢出异常
  3. 弱引用:比软引用更弱,只能生存到下一次垃圾回收时。WeekHashMap基于该引用实现,常用语缓存场景。
  4. 虚引用:不会对对象的生命产生任何影响,也无法通过虚引用获得对象实例,存在意义在于对象被清理时收到一个系统通知。

方法区的清理

方法区的清理不是必须的,因为在这里清理效率很低。
方法区的清理主要是清理废弃常量无用的类

  • 废弃常量是指常量池存在常量字段但没有任何地方引用该常量。
  • 无用的类判断方法比较繁琐,需要满足以下三个条件才能满足可以被回收的条件,注意仅仅是可以回收,不代表一定会被回收,具体是否回收还与虚拟机参数设置有关:
    1. 堆中不存在该类的实例。
    2. 加载该类的ClassLoader已被回收
    3. 该类的.class对象不存在任何引用,无法通过反射访问该类方法。

垃圾回收方法

引用计数法
一种很慢的垃圾回收方法。每个对象都含有一个引用计数器,当有引用连接至对象时,计数器+1,当引用离开作用域或被至为null时,计数器-1,当计数器归零时,释放空间。虽然计数开销不大,但该开销在整个程序生命周期中,且该方法对于循环引用(交互自引用)所需做的工作极大。

可达性分析
对任何活的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。从堆栈和静态存储区的GC Roots对象开始,遍历所有引用就能找到所有对象,能够解决交互自引用对象组的问题。
能够作为GC Roots对象的有虚拟机栈中引用的对象,方法区中类静态属性引用的对象、常量引用的对象,本地方法引用的对象。

对象到GC Roots不可达时,不是立刻被清理,需要经历两次标记。当对象被标记为即将回收时,虚拟机会检查该对象的finalize()是否已经被执行或者被覆盖,如果有被覆盖,该对象会被移入到F队列中,虚拟机使用finalize线程执行,对象在若在finalize()方法中重新连接引用,则不会被清除,否则,只能死翘翘了。特别提醒,finalize()方法只会被执行一次,若果本次回收时调用了该方法且成功逃离回收,若下次回收时再次面临被回收,也不会再调用该方法。

标记-清除法
对于一般的清楚而言慢,但若垃圾很少,则速度很快。它在遍历对象时给活的对象进行标记,当标记全部完成后,开始清理。清理后空间并不连续。
在这里插入图片描述

停止复制算法
暂停程序,将所有活的对象从当前对复制到另一个堆,没复制过来的就是垃圾。复制过去后是一个挨着一个的,保持紧凑排列,有利于分配新空间。同时对象的移动也对导致引用的失败,必须进行修正。该方法效率低,首先需要两个堆来回折腾,空间度X2,其次当程序稳定后垃圾很少,仍会进行复制操作,浪费。
在这里插入图片描述
目前商业虚拟机大多使用该方法,因为研究表明新生代对象98%都是“朝生夕死”的,内存划分不需要1:1,在HotSpot中,按照8:1:1的比例划分为伊甸区和两个幸存者区,每次新生代可用空间为伊甸区和一个幸存者区,占整个内存的90%,垃圾回收时将该部分的存活对象复制到剩下的幸存者区中,若容量不够,使用老年代作为辅助。

标记整理
对于老年代这种对象存活率较高的内存区域,使用复制法效果就不尽人意。标记整理法是基于标记清理法的,它在对可回收的对象进行标记后,不进行清理而是将存活的对象移动到一端,然后直接清理端界以外的内存。
在这里插入图片描述
自适应
当有大量对象需要清理时,采用停止-复制法;当稳定后切换到标记清扫;当堆空间出现很多碎片,切换回停止-复制法。

HotSpot算法

枚举根节点
从GC Roots根节点寻找引用链过程中,当方法区过大时,遍历寻找的时间复杂度很高,且该寻找过程需要保证时间一致性,即在寻找过程中应该是“时间冻结”的,对象引用关系不能发生变化,否则会影响分析结果准确性,因此该过程需要暂停所有java线程,产生GC停顿。

  • 对于遍历时间问题,HotSpot使用OopMap告诉虚拟机哪里有什么对象引用,在类加载的时候变将对象位置信息放在了OopMap中,虚拟机无需遍历整个上下文。但由于对象引用关系的变化,如果为每条指令都生成对于的OopMap,会带来巨大的空间开销,因此只会在特定的安全点生成这些信息。
  • 安全点是程序能够长时间运行的地方,如循环跳转,异常跳转,方法调用等,只有在这些指令出才能生成安全点,只有在安全点才能进行GC停顿。为了保证GC停顿时所有线程都能跑到最近的安全点处,有两种方法
    1. 抢先式中断
      GC时,所有线程中断,若线程没有跑到安全点,继续跑直到安全点。该方法基本没有使用的。
    2. 响应式中断
      GC时设置一个中断标志,标志位置与安全点相同,所有线程轮询该标志,当检测到中断标志时暂停等待。对于线程处于阻塞等状态无法运行的线程,使用安全区进行保证。
      安全区是指在一块代码块中引用关系不会发生变化,则该代码块就是安全区。线程执行到安全区后进行标志,可以进行GC停顿,当线程重新运行时先检测枚举根节点是否完成,若未完成继续等待。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值