Java中的强引用、软引用、弱引用和虚引用

区别

不同的引用类型,主要体现在对象的不同的可达性状态和对垃圾收集的影响。
如图
可达性状态图
JVM定义了不同级别的可达性,具体如下:
强可达:就当一个对象可以有一个或者多个线程可以不通过各种引用访问到的情况,比如我们new一个对象,那么创建他的线程对它就是强可达。
软可达:我们只能通过软引用才能访问到对象的状态。
弱可达:无法通过强引用或者软引用,只能通过弱引用访问时的状态。十分临近finalize状态。
幻象可达:和上述流程图描述一致没有强、软、弱的关联,并且对象已经被finalize过了。对象只有幻象引用。

强引用

强引用就是在Java类中我们普通定义时用的引用方式,比如:

A a = new A();

在Java里, 当一个对象被创建时, 它被放在Heap里. 当GC运行的时候, 如果发现没有任何引用指向a, a就会被回收以腾出内存空间. 或者换句话说, 一个对象被回收, 必须满足两个条件:
1)没有任何引用指向它
2)GC被运行.

在现实情况写代码的时候, 我们往往通过把所有指向某个对象的referece置空来保证这个对象在下次GC运行的时候被回收,如:

A a = new A();
a = null;

但是, 手动置空对象对于程序员来说, 是一件繁琐且违背自动回收的理念的. 对于简单的情况, 手动置空是不需要程序员来做的, 因为在java中, 对于简单对象, 当调用它的方法执行完毕后, 指向它的引用会被从stack中popup, 所以他就能在下一次GC执行时被回收了.
一般情况下,当内存中的对象不再有任何强引用指向它的时候,垃圾回收开始考虑进行垃圾回收。

软引用

定义一个类为软引用,则要用到SoftReference,SoftReference类是java.lang.ref.Reference;
1.Reference子类不能无参化直接创建,必须至少以强引用对象为构造参数,创建各自的子类对象;
2.因为1中以强引用对象为构造参数创建对象,因此,使得原本强引用所指向的堆内存中的对象将不再只与强引用本身直接关联,与Reference的子类对象的引用也有一定联系。且此种联系将可能影响到对象的垃圾回收。
3.Reference的子类提供了get方法,可以通过get方法获得原有的对象。这意味着,利用软引用和弱引用,我们可以将访问到的对象重新指向强引用。。这也是可达性状态图中强引用,弱引用和软引用之间是双向箭头的原因。而虚引用的get方法获得的对象只是null。

以SoftReference为例,其定义一个类为弱引用时

A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);

软引用是一种想对强应用弱化的一些引用,可以让对象豁免一些垃圾收集,只有当JVM认为内存不足时,才回去试图回收软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保存。当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
如以下代码

import java.lang.ref.SoftReference;

 public class ReferenceTest {

     public static void main(String[] args) {

         A a = new A();

         SoftReference<A> srA = new SoftReference<A>(a);

         a = null;

         if (srA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + srA.get());
         }

         // 垃圾回收
         System.gc();

         if (srA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + srA.get());
         }

     }
 }

 class A {

 }
代码出处:https://blog.csdn.net/o279642707/article/details/79237286 

运行后,可以看到结果:
在这里插入图片描述
这说明,即使当对象a被显示的置为null,即此对象没有强引用,而只有软引用的时候,我们提示虚拟机可以回收内存后,JVM没有回收a,说明JVM认为此事没必要清理此对象。JVM回收软引用对象需要满足两个条件
1.当前对象无任何强引用对象指向它
2.虚拟机当前内存不足
因此,SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。(因为如果对象只有强引用,那么如果该对象没有其他的任何引用,当系统进行垃圾回收时,很可能就直接被回收掉了)

弱引用

定义一个弱引用

A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);

弱引用在其引用的对象没有任何强引用对象时,此对象则进入正常的垃圾回收流程。如以下代码:

import java.lang.ref.SoftReference;

public class ReferenceTest {

     public static void main(String[] args) {

         A a = new A();

         WeakReference<A> wrA = new WeakReference<A>(a);

         a = null;

         if (wrA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + wrA.get());
         }

         // 垃圾回收
         System.gc();

         if (wrA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + wrA.get());
         }

     }

 }
 class A {

 }
代码出处:https://blog.csdn.net/o279642707/article/details/79237286 

运行结果:
在这里插入图片描述
可以看到,当对象没有强引用指向它时,JVM就可以对这个对象进行内存回收,WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。那其存在的意义是什么呢?

Java中的Cache机制就会用到弱引用。因为Cache中的对象正是程序运行时需要的,只要程序运行,Cache中的引用就不会被GC,(或者说, cache中的reference拥有了和主程序一样的life cycle),这样,Cache中的对象就会越来越多。而需要回收时,只能交给程序的编写者了。这就违背了GC自动回收的初衷。所以引入weak reference后,没有任何其他strong reference指向的时候, 如果GC运行, 那么这个对象就会被回收

在weak reference指向的对象被回收后, weak reference本身其实也就没有用了. java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference. 用法是在定义WeakReference的时候将一个ReferenceQueue的对象作为参数传入构造函数.

虚引用/幻象引用

你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。
要定义虚引用指向一个对象,则需要先介绍引用队列ReferenceQueue。由于虚引用所有的get()方法返回的都是null,如果没有引用队列,那指定虚引用就没有意义了。利用引用队列,我们可以在对象处于相应的状态时,执行一些自己想执行的逻辑。虚引用的定义为:

 A a = new A();
 ReferenceQueue<A> rq = new ReferenceQueue<A>();
 PhantomReference<A> prA = new PhantomReference<A>(a, rq);

引用队列ReferenceQueue

java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference. 用法是在定义SoftReference/WeakReference/PhantomReference的时候将一个ReferenceQueue的对象作为参数传入构造函数.以WeakReference为例

 public class ReferenceTest {

     public static void main(String[] args) {

         A a = new A();

         ReferenceQueue<A> rq = new ReferenceQueue<A>();
         WeakReference<A> wrA = new WeakReference<A>(a, rq);

         a = null;

         if (wrA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + wrA.get());
         }

         System.out.println("rq item:" + rq.poll());

         // 垃圾回收
         System.gc();

         if (wrA.get() == null) {
             System.out.println("a对象进入垃圾回收流程");
         } else {
             System.out.println("a对象尚未被回收" + wrA.get());
         }

         try {
             Thread.sleep(1);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }

         System.out.println("rq item:" + rq.poll());

     }
 }

 class A {

     @Override
     protected void finalize() throws Throwable {
         super.finalize();
         System.out.println("in A finalize");
     }
}

运行结果为:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值