Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为:

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

一、强引用(永不回收)

如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

public class StrongReferenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = o1;
        o1 = null;
        System.gc();
        System.out.println(o2); //java.lang.Object@14ae5a5
    }
}

 二、软引用(内存不足回收)

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

package cn.linwillen.base.ref;

import java.lang.ref.SoftReference;

/**
 * @author linwillen
 * @create 2022-01-10 0:19
 */
public class SoftReferenceDemo {

    /**
     * 当内存足够时
     */
    public static void softRefMemoryEnough(){
        Object o = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o);
        System.out.println(o); //java.lang.Object@14ae5a5
        System.out.println(softReference.get()); //java.lang.Object@14ae5a5

        o = null;
        System.gc();
        System.out.println(o); //null
        System.out.println(softReference.get()); //java.lang.Object@14ae5a5
    }

    public static void softRefMemoryNotEnough(){
        Object o = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o);
        System.out.println(o);
        System.out.println(softReference.get());

        o = null;
        try {
            byte[] bytes = new byte[30 * 1024 * 1024];
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            System.out.println(o);
            System.out.println(softReference.get());
        }

    }

    public static void main(String[] args) {
        // softRefMemoryEnough();
        softRefMemoryNotEnough();
    }
}

配置jvm内存大小为5m,并打印gc信息

 

 运行结果:

[GC (Allocation Failure) [PSYoungGen: 1024K->488K(1536K)] 1024K->568K(5632K), 0.0015137 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
java.lang.Object@14ae5a5
java.lang.Object@14ae5a5
[GC (Allocation Failure) [PSYoungGen: 1248K->488K(1536K)] 1328K->688K(5632K), 0.0011138 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 488K->488K(1536K)] 688K->704K(5632K), 0.0006997 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 488K->0K(1536K)] [ParOldGen: 216K->643K(4096K)] 704K->643K(5632K), [Metaspace: 3277K->3277K(1056768K)], 0.0066857 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 643K->643K(5632K), 0.0002589 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 643K->625K(4096K)] 643K->625K(5632K), [Metaspace: 3277K->3277K(1056768K)], 0.0072145 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
null
null
Heap
 PSYoungGen      total 1536K, used 65K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 6% used [0x00000000ffe00000,0x00000000ffe106c0,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 4096K, used 625K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
  object space 4096K, 15% used [0x00000000ffa00000,0x00000000ffa9c500,0x00000000ffe00000)
 Metaspace       used 3308K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 362K, capacity 388K, committed 512K, reserved 1048576K
java.lang.OutOfMemoryError: Java heap space
	at cn.linwillen.base.ref.SoftReferenceDemo.softRefMemoryNotEnough(SoftReferenceDemo.java:34)
	at cn.linwillen.base.ref.SoftReferenceDemo.main(SoftReferenceDemo.java:46)


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

当内存足够大时可以把数组存入软引用,取数据时就可从内存里取数据,提高运行效率。

软引用在实际中有重要的应用,例如浏览器的后退按钮,这个后退时显示的网页内容可以重新进行请求或者从缓存中取出:

  1. 如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建
  2. 如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出这时候就可以使用软引用

 三、弱引用(gc就回收)

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

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

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

package cn.linwillen.base.ref;

import java.lang.ref.WeakReference;

/**
 * @author linwillen
 * @create 2022-01-10 0:46
 */
public class WeakReferenceDemo {

    public static void main(String[] args) {
        Object o = new Object();
        WeakReference<Object> softReference = new WeakReference<>(o);
        System.out.println(o);
        System.out.println(softReference.get());
        System.out.println("=================");
        o = null;
        System.gc();
        System.out.println(o);
        System.out.println(softReference.get());
    }
}

运行结果

java.lang.Object@14ae5a5
java.lang.Object@14ae5a5
=================
null
null

知道WeakHashMap?

package cn.linwillen.base.ref;

import java.util.WeakHashMap;

/**
 * @author linwillen
 * @create 2022-01-10 0:55
 */
public class WeakHashMapDemo {

    public static void myWeakHashMap() {
        WeakHashMap<Integer, String> weakHashMap = new WeakHashMap<>();
        Integer key = new Integer(1);
        weakHashMap.put(key,"WeakHashMap");
        System.out.println(weakHashMap);
        key = null;
        System.out.println(weakHashMap);
        System.gc();
        System.out.println(weakHashMap+"\t"+weakHashMap.size());

    }

    public static void main(String[] args) {
        myWeakHashMap();
    }
}

运行结果

{1=WeakHashMap}
{1=WeakHashMap}
{}	0

四、虚引用(跟没的一样,一直是null)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动。

引用队列:对象被垃圾回收后,会将这个对象的引用放进队列里

package cn.linwillen.base.ref;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;

/**
 * @author linwillen
 * @create 2022-01-10 1:05
 */
public class ReferenceQueueDemo {

    public static void main(String[] args) {
        Object o = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        WeakReference<Object> weakReference = new WeakReference<Object>(o,referenceQueue);

        System.out.println(o);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("=====================");
        o = null;// 这一步是必须的,要设置为null对象才会被回收
        System.gc();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(o);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());


    }
}

 运行结果

java.lang.Object@14ae5a5
java.lang.Object@14ae5a5
null
=====================
null
null
java.lang.ref.WeakReference@7f31245a

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

package cn.linwillen.base.ref;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * @author linwillen
 * @create 2022-01-10 1:15
 */
public class PhantomReferenceDemo {

    public static void main(String[] args) {
        Object o = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(o,referenceQueue);

        System.out.println(o);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());


        o = null;
        System.out.println("===================");
        System.gc();

        System.out.println(o);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

    }
}

运行结果

java.lang.Object@14ae5a5
null
null
===================
null
null
java.lang.ref.PhantomReference@7f31245a

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐