jvm学习-jvm对象引用关系

文章内容,参考《深入理解java虚拟机》和 https://segmentfault.com/a/1190000037439801

强引用

对象中普遍存在的引用赋值:Object object = new Object() 这种。只要强引用关系还存在,那么垃圾回收期就永远不会回收,一般需要通过设置 null 值来进行回收。比如:object = null

软引用

对应的 java 类是 java.lang.ref.SoftReference 。当对象只有软引用可用(可到达)的时候,在垃圾回收后,内存不足的时候回再次发出垃圾回收,回收该对象,也可以配合引用队列来释放软引用本身
比如代码如下:

Object object = new Object();
// softRef 为软引用
// 另一个构造方法 SoftRenference(T referent, ReferenceQueue<? super T> q), 可传入引用队列来回收引用自身所占用的内存
SoftReference<Object> softRef = new SoftReference<>(object);
// 如果内存足够,get() 方法调用就会返回 object 对象,如果内存不足,GC 时会回收 softRef 所关联的对象 object ,此时 get() 调用就会返回 null
softRef.get();

软引用在实际中的应用,一般是为了避免内存溢出的发生。加入有一批图片资源,需要从磁盘中读取并缓存到内存中方便下次读取的话,而这部分的缓存并不是必须的,你不希望这部分缓存太大而导致内存溢出,那么你就可以考虑使用软引用。如下代码,堆内存只有20M ,需要读取5张 4M 的图片存入一个 list 中,分别演示强引用软引用

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

/**
 * 演示软引用
 * 配置jvm启动参数: -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GcDemo01 {

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

    public static void main(String[] args) throws IOException {
        //strong();
        soft();
    }

    /**
     * 使用强引用导致堆内存溢出 java.lang.OutOfMemoryError: Java heap space
     */
    public static void strong() {
        List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new byte[_4MB]);
        }
    }

    /**
     * 使用软引用,当内存不足垃圾回收时list中个别软引用对象所引用的byte[]对象会被回收,所以ref.get()可能返回null
     */
    public static void soft() {
        // list --> SoftReference --> byte[]
        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> softRef = new SoftReference<>(new byte[_4MB]);
            System.out.println(softRef.get());
            list.add(softRef);
            System.out.println(list.size());
        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

软引用代码执行结果

弱引用

弱引用对应的 java 类是 java.lang.WeakReference<T>,弱引用于软引用基本相同,区别是:仅有弱引用可达到对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用指向的对象。

虚引用

虚引用对应的 java 类是 java.lang.PhantomReference<T>,与软引用和弱引用不同的是,虚引用必须配合引用队列一起使用,因此只有一个构造方法 PhantomReference(T referent, ReferenceQueue<? super T> q),且虚引用的get()方法始终返回 null

虚引用实际上并不是来指向 java 对象的,所以 get() 方法始终返回 null,虚引用的作用主要体现在 释放直接内存。我们在使用 ByteBuffer.allocateDirect()申请分配直接内存时,会创建一个虚引用(Cleaner) 并且指向 ByteBuffer 对象,当 ByteBuffer 对象被回收的时候,虚引用(Cleaner) 会进入引用队列中,然后调用 Clean#clean() 方法来释放直接内存。

终结器引用-finalize()

终结器引用的 java 类是 java.lang.FinalReference<T>,也必须配合引用队列一起使用,终结器引用主要作用是在对象被真真回收之前,用来调用对象的finalize() 方法的。
当一个对象重写了 finalize() 方法,在垃圾回收的时候,终结器引用进入引用队列 ( 被引用对象暂时还没有被回收),再由 Finalizer 线程通过终结器引用找到了被引用对象并调用它的 finalize() 方法,第二次 GC 时才能回收被引用对象。

参考《深入理解java虚拟机第三版》的3.2.4可以知道,对象如果被可达性分析算法标记了过后,并不是马上就会被回收,首先虚拟机在可达性分析算法过程中,会对与 GC Roots 没有连接的对象进行第一次标记,然后等待第二次标记,如果在第二次标记的时候,还是没有与 GC Roots根对象进行连接,就会进行第二次标记,然后就肯定被回收了。所以,如果需要对象不被马上回收,可以重写 finalize() 方法。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值