垃圾回收算法

一、如何判断对象是否可以被回收

1、引用计数法

概念:在对象中添加一个引用计数器,每当一个引用指向该对象时,其引用计数器就加一;当引用失效时,其引用计数器就减一;当对象的引用计数器为0时,则该对象可以被回收。

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

缺点:存在对象间循环引用导致对象不能被回收的问题。

public class A {
	public B instanceB;
}

public class B {
	public A instanceA;
}

// a、b对象互相持有对方的一个对象引用,引用计数器都不为0,但是除了相互之间的引用并没有其他外部引用指向这两个对象
// a、b对象都可以回收,但是由于其引用计数器不为0,无法触发回收操作
public static void main(String[] args) {
    A a = new A();
    B b = new B();
    a.instanceB = b;
    b.instanceA = a;
}
2、可达性分析

概念:通过一系列称为"GC Roots"的根对象作为起始节点集,从这些节点开始根据引用关系向下搜索,搜索过程中所走过的路径被称之为"引用链",如果某个对象到GC Roots之间没有任何引用链相连,则说明该对象不可达,即不再被使用。

2.1、GC Roots 对象
  • 在虚拟机栈用引用的对象,如栈与堆中的局部变量、方法参数、临时变量等。
  • 在方法区中的静态对象、常量
  • 在本地方法栈中JNI (Native方法) 引用的对象
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象、常驻的异常对象(NullPointException、OutOfMemoryError等)、系统类加载器等
  • 所有被同步锁持有的对象
  • 存活的线程对象

在这里插入图片描述


2.2、什么是引用

jdk1.2版本之前,如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,那么就将该reference对象称之为是代表某块内存区域、某个对象的引用。

jdk1.2版本之后,java对引用的概念进行了扩充,解决了原先只能够定义被引用未被引用两种状态,而不能够定义当内存空间足够时能够被保留在内存中,而当内存空间不够时,可以被抛弃这种状态。

  • 强引用 Strongly Reference:强引用是传统的引用定义,代码中普遍出现的引用赋值就是强引用(比如 Object obj = new Object())。被强引用指向的对象,无论什么情况下都不会被垃圾回收器回收。
  • 软引用 Soft Reference:软引用用于描述一些有用、但是非必须的对象。被软引用指向的对象,只有在即将发生内存溢出之前,才会被纳入到回收范围中。如果回收后还是没有足够的内存,才会抛出内存溢出异常。
  • 弱引用 Weak Reference:弱引用用于描述非必须对象,它的强度比软引用还要弱些。被弱引用指向的对象,无论内存是否足够,都会在下次垃圾回收器工作时被回收。
  • 虚引用 Phantom Reference:虚引用是最弱的引用关系,一个对象是否有虚引用与其关联,完全不会对其生存时间产生影响,也无法通过虚引用来获取一个对象实例。其存在的目的只是为了能够在这个对象被回收时收到一个系统通知。

TheadLocal中使用弱引用解决了存放在ThreadLocal对象不会随着方法结束而被回收的问题

// 定义了一个ThreadLocal对象,并向其中存放了一个data字符串
public void testThreadLocal() {
 ThreadLocal<String> threadLocal = new ThreadLocal<>();
 threadLocal.set("data");
}

// ThreadLocalMap源码
static class ThreadLocalMap {
 static class Entry extends WeakReference<ThreadLocal<?>> {
     /** The value associated with this ThreadLocal. */
     Object value;

     Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
     }
 }
 ...
}

此时栈与堆中的引用关系应为如下图所示

在这里插入图片描述

随着方法的结束、栈帧的弹出,threadLocal引用也随之失效,正常来说ThreadLocal对象应当被回收。但是ThreadLocal对象还被ThreadLocalMap的Entry对象中的key属性引用,此时如果Entry对象中的key是强引用的话,则ThreadLocal对象无法被回收。因此Entry中的key被设计为弱引用,其所指向的ThreadLocal对象会在下次垃圾回收器工作时被回收。


二、被判定可回收后

1)对象在进行可达性分析后发现没有与GC Roots之间没有引用链相连,则会被第一次标记。

2)进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法,如果对象没有覆盖finalize()方法或finalize()方法已经被执行,则会被视为"没有必要执行"。

3)如果被判定为有必要执行finalize()方法,则该对象会放置在一个名为F-Queue的队列中,稍后会一条由虚拟机自行建立、低调度优先级的Finalizer线程去执行他们的finalize()方法。

4)进行第二次小规模标记,此时该对象仍旧没有与GC Roots之间没有引用链相连,就会被回收。

当执行到第三步的时候,如果对象的finalize()方法中,将自身与引用链上的任何一个对象建立了关联,就会在第四步时被移除"即将回收的集合",成功逃脱被回收的命运,但是下次再被标记由于finalize()方法已经被执行过,所以无法再次触发finalize()方法进行自救


三、回收方法区

方法区中被回收的对象主要有两部分:废弃的常量不再使用的类型

回收废弃常量与回收堆中的对象十分相似,比如一个字符串常量"java"在常量池中,当前系统中没有任何一个字符串对象的值是"java",且虚拟机中也没有其他地方引用这个字面量。此时如果发生垃圾回收,一旦垃圾回收器判断有必要的话,"java“常量就会被清理出常量池。

判断一个类是否可以被回收就比较麻烦,需要同时满足以下三个条件:

  • 该类的所有实例都已经被回收。

  • 加载该类的类加载器已经被回收。

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

满足上述三个条件后,Java虚拟机也仅仅是允许该类被回收,而不是必然被回收。


四、垃圾回收算法

当前大部分的垃圾收集器都是遵循了“分代收集”的理论进行设计,这个理论是建立在两个假说之上:

  • 弱分代假说:绝大部分对象都是朝生夕死

  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。

因此基于这两个假说奠定了大部分垃圾收集器的一致的设计原则:垃圾收集器应当将堆划分出不同的区域,然后将对象依据其年龄(熬过垃圾回收过程的次数)分配到不同的区域中进行存储

这样带来的好处就是:

  • 如果一个区域中大部分的对象都是朝生夕死,那么就可以将它们集中放在一起,每次回收时只关注如何保留少量的存活对象而不是大量将要被回收的对象,就能够以较低的代价回收大量的空间。
  • 如果一个区域中大部分对象都是难以消亡,那么就可以将它们集中放在一起,虚拟机可以使用较低的频率来回收这个区域,减少了性能的消耗。

正是因为上述原因,Java堆被划分出了不同的区域后,垃圾收集器就可以根据回收区域中所存储对象不同的存亡特性而采用不同的垃圾回收算法进行回收。由此出现了诸如标记清除算法标记整理算法标记复制算法等经典的垃圾回收算法。

4.1、标记清除算法

标记清除算法分为两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记对象。也可以反过来,标记存活对象,统一回收掉所有未被标记对象。

缺点:

  • 执行效率不稳定。如果Java堆中存在大量对象,其中大部分是需要被回收的,这时候就必须进行大量的标记和清除的动作,导致标记和清除这两个过程的执行效率都随着对象数量的增长而降低。
  • 会产生大量的内存碎片。标记、清除后会产生大量的内存碎片,内存碎片太多可能会导致随后程序在运行期间需要分配较大对象,无法找到足够的连续内存空间,而不得不触发一次垃圾回收动作。

在这里插入图片描述

4.2、标记整理算法

标记整理算法的标记过程与标记清除算法一致,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象往内存空间的一端移动,然后直接清理掉边界以外的内存。

优点:解决了内存碎片的问题。

缺点:如果存在大量的存活对象,将会产生大量的复制开销。

在这里插入图片描述

4.3、标记复制算法

标记复制算法将可用内存按照容量划分为了大小相等的两块,每次只使用其中的一块。当这块内存使用完了,就将在这块内存上还存活的对象复制到另外一块内存上,然后在把这块内存空间进行清空操作。

优点:解决了内存碎片的问题,内存分配时只需要移动堆顶的指针,按顺序分配即可,实现简单、运行高效。

缺点:

  • 可用内存空间只有50%,如果存在大量的存活对象,将会产生大量的复制开销。
  • 为了应对所有对象都存活的情况,需要其他的内存空间进行分配担保。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值