JVM(HotSpot):GC之垃圾标记阶段

时间:2024-10-21 21:16:20

文章目录

  • 前言
  • 一、标记阶段算法
    • 1、引用计数法
    • 2、可达性分析算法(JVM使用)
  • 二、4种引用
    • 1、 强引用
    • 2、软引用(SoftReference)
    • 3、弱引用(WeakHashMap)
    • 4、虚引用(PhantomReference)
  • 三、代码案例
    • 1、 强引用
    • 2、软引用(SoftReference)
    • 3、弱引用(WeakHashMap)

前言

我们从日常生活中应该知道,当我们去打扫一间屋子时,首先,你要判断那些事垃圾,然后,对其进行清除打扫。不然,把重要物品也给清除了,不是亏大了。

JVM垃圾回收器,也是这么工作的。
你可以把GC看作JVM这个屋子的清洁员。

那么,在打扫之前,需要精确的标记出哪些对象是垃圾对象,从而,为清理阶段做准备。

一、标记阶段算法

1、引用计数法

定义:一个对象,如果被引用了,那么,它就不能标记为垃圾,不能回收。

缺点:存在内存泄漏问题。
当两个垃圾对象互相引用时,无法回收。

2、可达性分析算法(JVM使用)

定义:对象直接或间接的被GC Root对象引用。则不能标记为垃圾。

哪些是GC Root对象
一般地
​ a、虚拟机栈中引用的对象
​ b、本地方法栈中引用的对象
​ c、方法区中静态变量引用的对象
​ d、字符串常量池中引用的对象
​ e、被sync锁把持的对象
​ f、局部回收和分代收集时候,产生的临时性GC Roots

二、4种引用

为什么要学习5中引用?
这个是在特定情况下,符合可达性分析算法时,依然要被回收的对象。
其中,软引用,弱引用,虚引用,终结器引用,它们也是对象,也占用着内存空间。
这几种引用,一般要结合引用队列进行回收处理。

终结器引用,无需手动编码。了解即可。
在这里插入图片描述

1、 强引用

​ 就是new出来的对象,即使内存不够了,也不能回收,会导致OOM

2、软引用(SoftReference)

​ 缓存技术常使用,内存不够了,才会回收相应的对象,它可以获取对象实例的方法和属性
可以配合引用队列来释放软引用自身

3、弱引用(WeakHashMap)

​ 内存够的情况下,只要GC发现,就会回收,它可以获取对象实例的方法和属性
可以配合引用队列来释放软引用自身

4、虚引用(PhantomReference)

​ 内存足够的情况下,只要GC就会被回收,无法获取对象实例的方法和属性,在于跟踪垃圾回收的过程
必须配合引用队列来释放软引用自身

三、代码案例

1、 强引用

运行参数设置
JVM内存大小设置为20M

-Xmx20m
    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();
    }

运行结果
在这里插入图片描述

2、软引用(SoftReference)

运行参数设置
JVM内存大小设置为20M,打印GC回收信息

-Xmx20m -XX:+PrintGCDetails -verbose:gc

大致引用关系: list --> SoftReference --> byte[]

    public static void soft() {
        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());
        }
    }

运行结果:没有发生OOM错误,触发了Full GC
在这里插入图片描述


使用引用队列,回收软引用

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());
        }

    }
}

运行结果:软引用被GC回收了。
在这里插入图片描述

3、弱引用(WeakHashMap)

运行参数设置
JVM内存大小设置为20M,打印GC回收信息

-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());
    }
}

运行结果
在这里插入图片描述