JVM篇 笔记2:垃圾回收


JVM篇 笔记1 :内存结构

https://blog.csdn.net/qq_46539281/article/details/120174709

JVM篇 笔记3:类加载与字节码技术

https://blog.csdn.net/qq_46539281/article/details/120174860

JVM篇 笔记4:内存模型

https://blog.csdn.net/qq_46539281/article/details/120174885


垃圾回收

  1. 如何判断对象可以回收
  2. 垃圾回收算法
  3. 分代垃圾回收
  4. 垃圾回收器
  5. 垃圾回收调优

1. 如何判断对象可以回收

1.1 引用计数法

有一个对象被其他的变量引用,就计数加1 , 多一个就加以。少一个就减一。为0 就可以被回收。

但是会出现一个闭端:

在这里插入图片描述
两个对象互相引用,就一直回收不了。


1.2 可达性分析算法

JVM 使用的判断释放是垃圾的算法。

先确定一系列的根对象,其他的对象是不是 根对象 直接间接的使用。 如果不是,就可以直接被垃圾回收。

  • Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以
    回收
  • 哪些对象可以作为 GC Root ?

在这里插入图片描述
使用该工具可以查看有哪些根对象,等等。


1.3 四种引用

  1. 强引用 (平时使用的引用,都是强引用)
    只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

  2. 软引用(SoftReference)
    仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用
    对象可以配合引用队列来释放软引用自身

  3. 弱引用(WeakReference)
    仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
    可以配合引用队列来释放弱引用自身

  4. 虚引用(PhantomReference)
    必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,
    由 Reference Handler 线程调用虚引用相关方法释放直接内存

  5. 终结器引用(FinalReference)
    无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象
    暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize
    方法,第二次 GC 时才能回收被引用对象


在这里插入图片描述

软引用:当没有直接根对象直接引用时,触发minor gc 时,是不会清理的。当 堆中的内存不够时,触发full gc 时,JVM认为这个虚引用不够重要,才把它当作垃圾进行回收了。

弱引用:当没有直接根对象直接引用时,只要触发了垃圾回收,就会把这个弱引用对象给回收了。


-Xmx20m -XX:+PrintGCDetails -verbose:gc 设置堆内存20m

import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示软引用
 * -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());
        }
    }
}

在这里插入图片描述
在堆中,已经生成了有4个 byte对象后,导致内存不够了,所以触发了full gc ,将前面4个软对象 的内存释放了,有空间后,就生成了第五个byte对象。所以打印后,就会出现这种情况。

当然,生成的虚引用也是一个对象,也是占内存的。并没有释放掉的。
需要释放的话,使用引用队列进行释放。


演示软引用, 配合引用队列 , 回收虚引用对象后,将虚引用也进行回收了。使用引用队列。

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示软引用, 配合引用队列
 */
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());
        }

    }
}

在这里插入图片描述
结果和上面的案例一样。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值