概述

Java 中的引用类似 C 语言中的指针,指向一个对象,比如:

// person 就是指向 Person 实例“张三”的引用Person person = new Person("张三");

在 JDK1.2 以前,Java 里的引用是很传统的定义:如果 reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该 reference 数据是代表某块内存、某个对象的引用

这种定义当然没有什么不对,但现在看来显得太狭隘了,比如我们希望描述一类对象:当内存空间足够时,能保留在内存中,如果内存空间在进行了垃圾收集后仍然紧张,则可以抛弃这些对象,很多系统的缓存功能都符合这样的应用场景

JDK1.2 对引用的概念作了补充,将引用分为强引用(Strongly Reference)、软引用(SoftReference)、弱引用(Weak Reference)和虚引用(Phantom Reference),强度依次减弱

强引用

Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。类似 Object obj = new Object()

当一个对象被强引用变量引用时,除非超过了引用的作用域或者显示地将相应强引用赋值为 null,否则是不可能被垃圾回收器回收的

软引用

软引用用来描述一些有用但非必须的对象,此类对象只有在进行一次垃圾收集仍然没有足够内存时,才会在第二次垃圾收集时被回收,需要用 java.lang.ref.SoftReference 类来实现

public class SoftRefenenceDemo {    public static void main(String[] args) {        softRefMemoryEnough();        System.out.println("------------");        softRefMemoryNotEnough();    }    private static void softRefMemoryEnough() {        Object o1 = new Object();        SoftReference s1 = new SoftReference(o1);        System.out.println(o1);  // java.lang.Object@2503dbd3        System.out.println(s1.get());  // java.lang.Object@2503dbd3        o1 = null;        System.gc();        System.out.println(o1);  // null        System.out.println(s1.get());  // java.lang.Object@2503dbd3    }     /**      * JVM配置 -Xms5m -Xmx5m ,故意 new 一个大对象,使内存不足产生 OOM,看软引用回收情况      */    private static void softRefMemoryNotEnough() {        Object o1 = new Object();        SoftReference s1 = new SoftReference(o1);        System.out.println(o1);  // java.lang.Object@4b67cf4d        System.out.println(s1.get());  // java.lang.Object@4b67cf4d        o1 = null;                try {          byte[] bytes = new byte[10 * 1024 * 1024];        } catch(Error e) {          e.printStackTrace();        }                System.out.println(o1);  // null        System.out.println(s1.get());  // null    }}

弱引用

弱引用用来描述那些非必须对象,但它的强度比软引用更弱一些。被软引用关联的对象只能生存到下一次垃圾收集发生为止,当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象,需要用 java.lang.ref.WeakReference 类来实现

public class WeakReferenceDemo {    public static void main(String[] args) {        Object o1 = new Object();        WeakReference w1 = new WeakReference(o1);        System.out.println(o1);  // java.lang.Object@7440e464        System.out.println(w1.get());  // java.lang.ref.WeakReference@49476842        o1 = null;        System.gc();        System.out.println(o1);  // null        System.out.println(w1.get());  // null    }}

虚引用

虚引用,顾名思义,就是形同虚设,与其他几种引用都不太一样,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列(RefenenceQueue)联合使用。虚引用的主要作用是跟踪对象垃圾回收的状态,仅仅是提供了一种确保对象被 finalize 以后,收到一个系统通知或者后续添加进一步的处理

public class PhantomReferenceDemo {    public static void main(String[] args) throws InterruptedException {        Object o1 = new Object();        ReferenceQueue referenceQueue = new ReferenceQueue();        PhantomReference phantomReference = new PhantomReference(o1,referenceQueue);        System.out.println(o1);  // java.lang.Object@7440e464        System.out.println(referenceQueue.poll());  // null        System.out.println(phantomReference.get());  // null        o1 = null;        System.gc();        Thread.sleep(3000);        System.out.println(o1);  // null        System.out.println(referenceQueue.poll()); // java.lang.ref.PhantomReference@49476842        System.out.println(phantomReference.get());  // null    }}

ReferenceQueue 是用来配合引用工作的,没有ReferenceQueue 一样可以运行。SoftReference、WeakReference、PhantomReference 都有一个可以传递 ReferenceQueue 的构造器。创建引用的时候,可以指定关联的队列,当 GC 释放对象内存的时候,会将引用加入到引用队列。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,这相当于是一种通知机制。当关联的引用队列中有数据的时候,意味着指向的堆内存中的对象被回收。通过这种方式,JVM 允许我们在对象被销毁后,做一些我们自己想做的事情

Copyright © maxssl.com 版权所有 浙ICP备2022011180号