JVM系列之垃圾回收算法

一、如何判断某个对象是否为“垃圾”

1.引用计数算法

在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效计数值就减一;任何时刻计数器为零的对象就是不可能在被使用。

引用计数算法虽然占用了一些额外的内存空间来进行计数,但原理简单,判断效率也很高,但是无法检测出循环引用。

2.可达性分析算法

基本思路就是通过一系列称为“GC Roots"的根对象作为起始节点,从这些结点开始,根据引用关系向下搜索,搜索过程所走过的路径成为”引用链“,如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能在被使用的。

在Java语言中,可作为GC Roots的对象包括下面几种:

1.虚拟机栈中引用的对象(栈帧中的本地变量表;

2.方法区中类静态属性引用的对象;

3.方法区中常量引用的对象;

4.本地方法栈中JNI(Native方法)引用的对象。

二、再谈引用

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在Java语言中,将引用又分为强引用、软引用、弱引用、虚引用4种,这四种引用强度依次逐渐减弱。

强引用

在程序代码之中普遍存在的引用赋值,即类似”Object obj=new Object()"这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用

用来描述一些还有用,但非必须对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次内存回收还没有足够的内存,才会抛出内存溢出异常。

弱引用

也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

虚引用

也成为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

三、生存还是死亡

对象死亡(被回收)前的最后一次挣扎

  即使在可达性分析算法中不可达的对象,也并非是“非死不可”,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程。

  第一次标记:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记;

  第二次标记:第一次标记后接着会进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。在finalize()方法中没有重新与引用链建立关联关系的,将被进行第二次标记。

  第二次标记成功的对象将真的会被回收,如果对象在finalize()方法中重新与引用链建立了关联关系,那么将会逃离本次回收,继续存活。

四、 方法区如何判断是否需要回收

  方法区的垃圾收集主要回收两部分内容:废弃常量和无用的类。对于废弃常量也可通过引用的可达性来判断,但是对于无用的类则需要同时满足下面3个条件:

1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;

2.加载该类的ClassLoader已经被回收;

3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

五、垃圾收集算法

1.标记-清除算法

该算法分为“标记”和“清楚”两个阶段:首先标记所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有被标记的对象。但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片。

2.标记-复制算法

复制算法的提出是为了克服句柄的开销和解决内存碎片的问题。它开始时把堆中可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。如果内存中多数对象都是存活的,这种算法将会产生大量的内存间复制的开销,但对于多数对象都是可回收的情况,算法需要复制的就是占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。简单,运行高效,不过可用内存缩小了一半。

3.标记-整理算法

 记录过程和标记-清楚算法一样,但后续步骤为,让所有存活的对象都向一端移动,直接清理掉边界以外的内存。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,成本更高,但解决了内存碎片的问题。

4.分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值