前言

今天在看ThreadLocal源码的时候,其中内部类ThreadLocalMap中的Entry继承了WeakReferenc,为了能搞清楚ThreadLocal,只能先了解下了WeakReferenc。

先看一下源码:

关于Java中的WeakReference类的笔记(弱引用)_弱引用

从源码中可以看出他又继承了抽象类Reference,WeakReference如字面意思,弱引用, 当一个对象仅仅被weak reference(弱引用)指向, 而没有任何其他strong reference(强引用)指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。

WeakReference(T referent):referent就是被弱引用的对象,可以如下使用,并且通过get()方法来获取引用:

        Student student = new Student("hello");
        WeakReference<Student> appleWeakReference = new WeakReference<>(student);
        Student s = appleWeakReference.get();
        System.out.println(s);

WeakReference(T referent, ReferenceQueue<? super T> q):这个构造方法多了个ReferenceQueue参数,在对象被回收后,会把弱引用对象,也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中,注意不是被弱引用的对象,被弱引用的对象已经被回收了。

面是使用继承WeakReference的方式来使用软引用,并且不使用ReferenceQueue:

新建一个student类:

package com.zoo.api.modules.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

/**
 * @author: 谢飞
 */
@Data
@AllArgsConstructor
@ToString
public class Student {
    private String name;

    /**
     * 覆盖finalize,在回收的时候会执行。
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println(name + "对象正在回收finalize");
    }
}

继承WeakReference的Person类:

package com.zoo.api.modules.test.entity;

import java.lang.ref.WeakReference;

/**
 * 这里Person继承WeakReference,将Student作为弱引用。注意到时候回收的是Student,而不是Person
 *
 * @author: 谢飞
 */
public class Person extends WeakReference<Student> {
    public Person(Student referent) {
        super(referent);
    }
}

测试:

    public static void main(String[] args) throws InterruptedException {
        Person person = new Person(new Student("张三"));
        //通过WeakReference的get()方法获取Apple
        System.out.println("Student:" + person.get());
        System.gc();
        //休眠一下,在运行的时候加上虚拟机参数-XX:+PrintGCDetails,输出gc信息,确定gc发生了。
        Thread.sleep(5000);
        System.out.println("gc回收之后的Student:" + person.get());
    }

控制台打印:

Student:Student(name=张三)
[GC (System.gc()) [PSYoungGen: 3939K->1240K(37888K)] 3939K->1248K(123904K), 0.0009368 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 1240K->0K(37888K)] [ParOldGen: 8K->1107K(86016K)] 1248K->1107K(123904K), [Metaspace: 3389K->3389K(1056768K)], 0.0037127 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
张三对象正在回收finalize
gc回收之后的Student:null
Heap
 PSYoungGen      total 37888K, used 4260K [0x00000000d6100000, 0x00000000d8b00000, 0x0000000100000000)
  eden space 32768K, 13% used [0x00000000d6100000,0x00000000d6529188,0x00000000d8100000)
  from space 5120K, 0% used [0x00000000d8100000,0x00000000d8100000,0x00000000d8600000)
  to   space 5120K, 0% used [0x00000000d8600000,0x00000000d8600000,0x00000000d8b00000)
 ParOldGen       total 86016K, used 1107K [0x0000000082200000, 0x0000000087600000, 0x00000000d6100000)
  object space 86016K, 1% used [0x0000000082200000,0x0000000082314f20,0x0000000087600000)
 Metaspace       used 3977K, capacity 4572K, committed 4864K, reserved 1056768K
  class space    used 435K, capacity 460K, committed 512K, reserved 1048576K

 从上面代码看,执行gc后,Student确实被回收了。

ReferenceQueue的使用:

测试:

public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Student> referenceQueue = new ReferenceQueue<>();
        WeakReference<Student> weakReference1 = new WeakReference<>(new Student("张三"), referenceQueue);
        WeakReference<Student> weakReference2 = new WeakReference<>(new Student("李四"), referenceQueue);

        //不会输出,因为没有回收被弱引用的对象,并不会加入队列中
        System.out.println("gc调用前的referenceQueue:" + referenceQueue.poll());
        System.out.println(weakReference1.get());
        System.out.println(weakReference2.get());

        System.out.println("=====调用gc=====");
        System.gc();

        Thread.sleep(5000);

        System.out.println("=====gc调用后=====");
        //下面两个输出为null,表示对象被回收了
        System.out.println(weakReference1.get());
        System.out.println(weakReference2.get());

        //输出结果,再次证明对象被回收了
        //如果使用继承的方式就可以包含其他信息了
        System.out.println("appleReferenceQueue中:" + referenceQueue.poll());
    }

可以看到在队列中(ReferenceQueue),调用gc之前是没有内容的,调用gc之后,对象被回收了,并且弱引用对象weakReference1和weakReference2被放入了队列中。

关于其他三种引用,强引用、软引用、虚引用,具体可以自己百度了解。

Java 4种引用的级别由高到低依次为:

    强引用  >  软引用  >  弱引用  >  虚引用

关于Java中的WeakReference类的笔记(弱引用)_强引用_02

总结:

java区别于C/C++的一个重要不同点就是Java不需要程序员手动申请或释放内存,JVM通过GC(Garbage-Colletion)(GC实际是一条专门用户回收Java堆内存中无用对象的线程)帮我们完成了垃圾的回收操作,所以程序员不必担心内存的释放问题,也就是不用手动管理对象的生命周期了。

但这也并不表示说Java程序员完全不必担心内存问题了,比方说我们new了很多对象,而每个对象的生命周期又非常的长,长到就算内存不足了GC也没办法去回收掉一些对象来释放更多的内存,这样最终就会导致OOM(Out-Of-Memory)错误。