Java学习-引用总结

版权声明:欢迎转载,请注明作者和出处 https://blog.csdn.net/baichoufei90/article/details/83550049

Java学习-引用总结

0x01 摘要

本篇文章主要是总结下java中的各种引用即:强引用、软引用、弱引用、虚引用。

0x02 强引用

引用是JAVA中默认采用的一种方式,我们平时创建的引用都属于强引用。
如果一个对象没有强引用,那么对象就可能会被回收。

使用强引用一定要注意避免内存泄露。

测试代码如下:

    public void strongReferenceTest(){
        Object obj = new Object();
        Object objRef = obj;
        obj = null;
        System.gc();
        System.out.println(obj);
    }

0x03 软引用

如果一个对象只具有软引用那这个对象有以下特点:

  • 内存空间足够时,垃圾回收器不会回收它。也就是说 即使发生了gc 但是还有大量内存可用,那也不会立刻回收软引用
  • 如果内存空间不足,就会回收这些对象的内存。
  • 只要垃圾回收器没有回收它,该对象就可以被程序使用。
  • 软引用可用来实现内存敏感的高速缓存。

示例如下:

    public void softReferenceTest(){
        String str=new String("abc");                                     // 强引用
        SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用
        str = null;
//        当内存不足时,等价于:
       /* If(JVM.内存不足()) {
            str = null;  // 转换为软引用
            System.gc(); // 垃圾回收器进行回收
        }*/
    }

0x04 弱引用

4.1 弱引用概念

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。

当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,此时最佳方式就是弱引用。

在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联 的引用队列中。

4.2 弱引用示例

示例代码如下:

    public void weakReferenceTest(){
    	// str强引用
        String str = new String("hello"); 
        // 引用队列
        ReferenceQueue<String> rq = new ReferenceQueue<String>(); 
        // 弱引用,关联了str rq队列
        WeakReference<String> wf = new WeakReference<String>(str, rq); 
        // 取消"hello"对象的强引用
        str=null;
        // 强制gc
        // 注意,实际场景中要慎用 System.gc(); 会导致FUll gc即整个heap gc
        System.gc();
        // 假如"hello"对象没有被回收,str1会强引用"hello"对象
        String str1=wf.get(); 
        System.out.println("str1 = " + str1);
        // 假如"hello"对象没有被回收,rq.poll()返回null
        Reference<? extends String> ref=rq.poll();
        System.out.println("ref = " + ref);
    }

4.3 WeakHashMap

WeakHashMap是一个特殊的map,有着弱引用的key。也就是说,当某个key没有再被正常使用时,WeakHashMap中的这个key对应的entry会被自动移除。更准确的说,一个拥有一个指定的key的mapping映射存在也不能阻止该key被GC回收。当一个key被GC回收掉后,他的entry会从该Map中被高效移除掉。这一点是WeakHashMap与其他map最大的不同之处。

当使用 WeakHashMap 时,即使没有显示的添加或删除任何元素,也可能发生如下情况:

  • 调用两次size()方法返回不同的值;
  • 两次调用isEmpty()方法,第一次返回false,第二次返回true;
  • 两次调用containsKey()方法,第一次返回true,第二次返回false,尽管两次使用的是同一个key;
  • 两次调用get()方法,第一次返回一个value,第二次返回null,尽管两次使用的是同一个对象。

WeakHashMap是软引用的一个典型的应用,更多信息请点击Java学习-容器-WeakHashMap

0x05 虚引用

虚引用形同虚设,它所引用的对象随时可能被垃圾回收。

虚引用的使用场景很窄,在JDK中,目前只知道在申请堆外内存时有它的身影。申请堆外内存时,在JVM堆中会创建一个对应的Cleaner对象,这个Cleaner类继承了PhantomReference,当DirectByteBuffer对象被回收时,可以执行对应的Cleaner对象的clean方法,做一些后续工作,这里是释放之前申请的堆外内存。

由于虚引用的get方法无法拿到真实对象,所以当你不想让真实对象被访问时,可以选择使用虚引用,
它唯一能做的是在对象被GC时,收到通知,并执行一些后续工作。

看到有些文章说虚引用可以清理已经执行finalize方法,但是还没被回收的对象,这简直就是误导人嘛,与finalize方法有关的引用是FinalReference,这个引用就是之前说的其它两种中的一个。

0x06 综合例子

在以下例程的References类中,依次创建了10个软引用、10个弱引用和10个虚引用,它们各自引用一个Grocery对象。

import java.lang.ref.*;
import java.util.*;
class Grocery {
    private static final int SIZE = 10000;
    // 属性d使得每个Grocery对象占用较多内存,有80K左右
    private double[] d = new double[SIZE];
    private String id;
    public Grocery(String id) {
        this.id = id;
    }
    public String toString() {
        return id;
    }
    // 通过finalize方法观察该对象被gc认为可以回收
    public void finalize() {
        System.out.println("Finalizing " + id);
    }
}
public class References {
    private static ReferenceQueue<Grocery> rq = new ReferenceQueue<Grocery>();
    public static void checkQueue() {
        // 从队列中取出一个引用
        Reference<? extends Grocery> inq = rq.poll();
        if (inq != null)
            System.out.println("In queue: " + inq + " : " + inq.get());
    }
    public static void main(String[] args) {
        final int size = 10;
        // 创建10个Grocery对象以及10个软引用
        Set<SoftReference<Grocery>> sa = new HashSet<SoftReference<Grocery>>();
        for (int i = 0; i < size; i++) {
            SoftReference<Grocery> ref = new SoftReference<Grocery>(
                    new Grocery("Soft " + i), rq);
            System.out.println("Just created: " + ref.get());
            sa.add(ref);
        }
        System.gc();
        checkQueue();
        // 创建10个Grocery对象以及10个弱引用
        Set<WeakReference<Grocery>> wa = new HashSet<WeakReference<Grocery>>();
        for (int i = 0; i < size; i++) {
            WeakReference<Grocery> ref = new WeakReference<Grocery>(
                    new Grocery("Weak " + i), rq);
            System.out.println("Just created: " + ref.get());
            wa.add(ref);
        }
        System.gc();
        checkQueue();
        // 创建10个Grocery对象以及10个虚引用
        Set<PhantomReference<Grocery>> pa = new HashSet<PhantomReference<Grocery>>();
        for (int i = 0; i < size; i++) {
            PhantomReference<Grocery> ref = new PhantomReference<Grocery>(
                    new Grocery("Phantom " + i), rq);
            System.out.println("Just created: " + ref.get());
            pa.add(ref);
        }
        System.gc();
        checkQueue();
    }
}

从程序运行时的打印结果可以得出以下结论:

  • 虚引用形同虚设,它所引用的对象随时可能被垃圾回收
  • 具有弱引用的对象拥有稍微长的生命周期,当垃圾回收器执行回收操作时,有可能被垃圾回收
  • 具有软引用的对象拥有较长的生命周期,但在Java虚拟机认为内存不足的情况下,也会被垃圾回收

0x07 总结

java引用

0xFF 参考文档

Java中软引用、弱引用和虚引用的使用方法示例
java强引用,软引用,弱引用,虚引用

猜你喜欢

转载自blog.csdn.net/baichoufei90/article/details/83550049