1.简介

  在JDK1.2之后,Java对引用的概念做了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用的强度依次递减。

 

1)强引用(StrongReference)
  强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。  ps:强引用其实也就是我们平时A a = new A()这个意思。


2)软引用(SoftReference)
  如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,会把这些对象列入回收范围进行第二次回收。软引用可用来实现内存敏感的高速缓存。
  
3)弱引用(WeakReference)
  弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
  
4)虚引用(PhantomReference)
  “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

2 强引用

 强应用就是我们日常使用的。

 

 2.1 代码示例

//重写了finalize方法,这个方法会在这个类的对象被回收的时候被调用,且只能被调用一次
public
class TestPo { @Override public void finalize(){ System.out.println("TestPo对象被回收,finalize方法被调用"); } }
public static void main(String[] args) throws InterruptedException {
        TestPo po = new TestPo();
        System.gc();
        Thread.sleep(500);//防止垃圾收回没结束,进程就结束了
        System.out.println(po);

    }

po指向一个TestPo对象,这就是一个强应用

 

执行结果:com.ruoyi.weixin.user.ThreadLocalTest.TestPo@4783da3f

对象没有被回收

 

2.2 示例代码

 public static void main(String[] args) throws InterruptedException {
        TestPo po = new TestPo();
        System.gc();
        Thread.sleep(500);
        System.out.println(po);
        po = null;
        System.gc();
        Thread.sleep(500);
    }
po = null,导致引用没有了,对象会被回收

执行结果,finalize方法被调用,说明对象被回收(注意,在finalize中也可以重新使变量指向TestPo,使它不会被回收)

com.ruoyi.weixin.user.ThreadLocalTest.TestPo@4783da3f
TestPo对象被回收,finalize方法被调用
 

3 软引用

  软引用需要通过SoftReference来创建

 

3.1 示例

public static void main(String[] args) throws InterruptedException {
        TestPo po = new TestPo();
        System.gc();
        Thread.sleep(500);
        System.out.println(po);
        po = null;
        System.gc();
        Thread.sleep(500);
    }

 执行结果,数组没有被回收

[B@4783da3f
[B@4783da3f

 

3.2 示例

在3.1的基础上,又创建了一个15M的数组

public static void main(String[] args) throws InterruptedException {
        SoftReference<byte[]> sf = new SoftReference(new byte[1024 * 1024 * 10]);
        System.out.println(sf.get());
        System.gc();
        Thread.sleep(500);
        System.out.println(sf.get());

        byte[] b = new byte[1024 * 1024 * 12];
        Thread.sleep(500);
        System.out.println(sf.get());
    }

执行结果,还是没有回收

[B@4783da3f
[B@4783da3f
[B@4783da3f

 

3.3 示例

3.1的代码不变,设置下虚拟机的堆内存

public class RuanTest {
    public static void main(String[] args) throws InterruptedException {
        SoftReference<byte[]> sf = new SoftReference(new byte[1024 * 1024 * 10]);
        System.out.println(sf.get());
        System.gc();
        Thread.sleep(500);
        System.out.println(sf.get());

        byte[] b = new byte[1024 * 1024 * 12]; //注意,这里数值不要太大,不然会报OOM,开始15M的时候就报了(JDK1.8)
        System.out.println(sf.get());
    }
}

 执行结果,发现软引用指向的数组已经被回收了。因为内存不够用了,所以它被回收

[B@4783da3f
[B@4783da3f
null

 

3.4 软引用的引用场景

  它非常适合缓存使用

 

4 弱引用

  弱引用通过WeakReference创建

 

4.1 示例

 public static void main(String[] args) {
        WeakReference<TestPo> wr = new WeakReference<TestPo>(new TestPo());
        System.out.println(wr.get());
        System.gc();
        System.out.println(wr.get());
    }

执行结果,垃圾回收直接把弱引用指向的对象回收掉了

com.ruoyi.weixin.user.ThreadLocalTest.TestPo@4783da3f
null

 

4.2 弱引用的实际应用ThreadLocal

https://www.cnblogs.com/jthr/p/14047761.html

 

5 虚引用(若引用在最后)

  需引用相当于没有,它只是起到一个通知的作用

 

5.1 通常JVM使用数据 

通常情况下,外部数据会先到达操作系统,保存在操作系统管理的内存,JVM想要使用数据,它会复制一份放到JVM里面

  

 

5.2 JVM使用直接内存

JVM还有这种模式,JVM访问直接内存。它不去复制数据到JVM里面,而是通过一个指针直接访问操作系统管理的内存里面的数据

 虚拟机的垃圾回收器只能回收JVM的内存。那么当上图中JVM中的对象被回收的时候,怎么回收操作系统管理的内存里面的数据呢?它就是通过虚引用的机制来完成的?

 

5.3 示例

虚引用需要通过PhantomReference来创建

public class XuTest {
    private static final List<Object> LIST = new LinkedList<Object>();

    private static final ReferenceQueue<TestPo> QUENE = new ReferenceQueue<TestPo>();

    public static void main(String[] args) {
        PhantomReference<TestPo> pr = new PhantomReference<TestPo>();
        System.out.println(pr.get());
    }
}

 执行结果,发现根本获取不到

null

 

5.4 示例

public class XuTest {
    private static final List<Object> LIST = new LinkedList<Object>();

    private static final ReferenceQueue<TestPo> QUENE = new ReferenceQueue<TestPo>();

    public static void main(String[] args) {
        PhantomReference<TestPo> pr = new PhantomReference<TestPo>(new TestPo(), QUENE);
        System.out.println(pr.get());

      //向LIST中不断的放入1M的数组
new Thread(()->{ while (true){ LIST.add(new byte[1024 * 1024]); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } System.out.println(pr.get()); } }).start();
     //不断的去QUENE中去看,有没有数据
new Thread(()->{ while (true){ Reference<? extends TestPo> poll = QUENE.poll(); if(poll != null){ System.out.println("虚引用对象被JVM回收了" + poll); } } }).start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }

设置堆内存最大为20M

执行结果

null
TestPo对象被回收,finalize方法被调用
null
null
null
null
null
null
null
null
虚引用对象被JVM回收了java.lang.ref.PhantomReference@6ce865f6
null
null
null
null
null
null
null
null
null
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    at com.ruoyi.weixin.user.ThreadLocalTest.XuTest.lambda$main$0(XuTest.java:26)
    at com.ruoyi.weixin.user.ThreadLocalTest.XuTest$$Lambda$1/758705033.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

当虚引用指向的对象要被回收的时候,它会先把这个对象的引用放入队列里面,然后来进行回收。这个QUENE就相当于起一个通知的作用,表示我这个虚引用指向的对象要被回收了,你要进行什么处理就到队列中去处理。(简单来说虚引用的作用就是在它指向的对象被回收的时候维护了一个QUENE,可以知道它要被回收)

现在就可以解决4.2中的问题了,我们创建一个虚引用对象,虚引用对象里面有指针指向操作系统管理的内存。当这个对象要被回收时,它的引用会放入QUENE里面,虚拟机会去QUENE中获取到这个对象的引用,回收JVM中这个对象,并且回收这个对象中的指针指向操作系统中的内存的数据。