什么是垃圾回收?

程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占有内存资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。

C/C++语言的垃圾回收

  • 在C/C++语言中,没有自动垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内存资源
  • 如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终可能会导致内存溢出

Java语言的垃圾回收

  • 为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC
  • 有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。
  • 换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能会导致内存溢出的。
  • 当然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。
  • 内存的释放交给JVM

五种引用

1. 强引用

  • 一般的引用都是强引用,如“Object o = new Object();”
  • 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
  • 只要能有沿着GC Root引用链找到,就不会被回收

2. 软引用(SoftReference)

  • GCroot强引用到了软引用对象,软引用对象在引用的对象被称作软引用
  • 只要没有被强引用都有可能被回收
  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收(第二次GC),回收软引用对象可以配合引用队列来释放软引用自身

3. 弱引用(WeakReference)

  • 只要没有被强引用都有可能被回收
  • 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象(第一次GC),可以配合引用队列来释放弱引用自身

4. 虚引用(PhantomReference)

  • 如直接内存地址Clearner对象
  • 无法获取对象的实例,唯一的作用就是该对象被回收时会得到一个通知
  • 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

5. 终结器引用(FinalReference)

  • 当一个对象写了finallize()方法,当该对象没有被强引用,它终结器对象会进入引用队列
  • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象
  • Finalizer 线程优先级很低,容易造成对象迟迟不被释放,不建议使用finallize()方法

引用队列:

  • 如果软引用的对象或者弱引用对象等所软引用或者弱引用的对象被垃圾回收掉,则会进入引用队列,如A2、A3对象被释放。
  • 因为这些对象,被GCroot强引用,可以通过该方式,释放这些对象

软引用场景案例代码

/** * 演示软引用 * -Xmx20m -XX:+PrintGCDetails -verbose:gc */
public class Demo2_3 {

    private static final int _4MB = 4 * 1024 * 1024;



    public static void main(String[] args) throws IOException {
        /*List<byte[]> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { list.add(new byte[_4MB]); } System.in.read();*/
        soft();


    }
	//软引用
    public static void soft() {
        // list --> SoftReference --> byte[]

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());

        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

软引用配合引用队列

/** * 演示软引用, 配合引用队列 */
public class Demo2_4 {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }

    }
}

弱引用场景代码

/** * 演示弱引用 * -Xmx20m -XX:+PrintGCDetails -verbose:gc */
public class Demo2_5 {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        // list --> WeakReference --> byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
            list.add(ref);
            for (WeakReference<byte[]> w : list) {
                System.out.print(w.get()+" ");
            }
            System.out.println();

        }
        System.out.println("循环结束:" + list.size());
    }
}