“强软弱虚”四种引用

本文详细介绍了Java内存管理中的四种引用类型:强引用、软引用、弱引用和虚引用。强引用是最常见的引用,除非显式设置为null,否则不会被垃圾收集器回收。软引用在内存不足时会被回收,常用于缓存。弱引用在GC时总会被回收,而虚引用则更弱,无法通过get获取对象,仅用于跟踪回收过程。通过示例代码展示了各种引用类型的使用和效果。
摘要由CSDN通过智能技术生成

强引用

Object o = new Object();这就是强引用,只要某个对象有强引用与之关联,这个对象永远不会被回收,即使内存不足,JVM宁愿抛出OOM,也不回去回收,只有当强引用和对象之间的关联切断了,就可以被回收了,回收特别简单,只需要将o = null;

public class Student {
  //当垃圾收集器认为没有指向对象实例的引用时,会在销毁该对象之前调用finalize()方法。
  @Override
  protected void finalize() throws Throwable {
    System.out.println("Student被回收了");
  }

  public static void main(String[] args) {
    Student student = new Student();
    student=null;
    System.gc();
  }
}

在这里插入图片描述
在实际的开发中,看到有一些对象被手动赋值为NULL,很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了。

软引用

软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下。。当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。

public class SoftRefeTest {
  public static void main(String[] args) {
  	//在堆内存中开辟10M的空间
    SoftReference<byte[]> sf = new SoftReference<byte[]>(new byte[1024 * 1024 * 10]);
    System.out.println(sf.get());
    System.gc();
    System.out.println(sf.get());
    //在堆内存中再开辟10M的空间
    byte[] bytes = new byte[1024 * 1024 * 10];
    System.out.println(sf.get());
  }
}

设置最大堆内存20M。
在这里插入图片描述
第一次GC,只占用了10M,空间很足,不会回收,第二次GC,空间不够了,jvm回收软引用指向的对象
在这里插入图片描述
每一个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过这个引用队列,可以跟踪对象的回收情况

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class SoftRefQ {
  public static class User {
    public int id;
    public String name;

    public User(int id, String name) {
      this.id = id;
      this.name = name;
    }

    @Override
    public String toString() {
      return "User{" +
        "id=" + id +
        ", name='" + name + '\'' +
        '}';
    }
  }
  public static class UserSoftReference extends SoftReference<User>{
    int uid;
    public UserSoftReference(User referent, ReferenceQueue<? super User> q) {
      super(referent, q);
      uid=referent.id;
    }

  }
  static ReferenceQueue<User> softQueue=null;
  public static class CheckRefQueue extends Thread{
    @Override
    public void run() {
      while (true) {
        if(softQueue != null) {
          UserSoftReference obj=null;
          try {
            obj = (UserSoftReference)softQueue.remove();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          if (obj!=null) {
            System.out.println("user id "+obj.uid+"is delete");
          }
        }
      }
    }
  }

  public static void main(String[] args) throws  InterruptedException{
    Thread t = new CheckRefQueue();
    t.setDaemon(true);
    t.start();
    User user = new User(1, "geym");
    softQueue = new ReferenceQueue<User>();
    UserSoftReference userSoftReference = new UserSoftReference(user, softQueue);
    user=null;
    System.out.println(userSoftReference.get());
    System.gc();
    System.out.println("After GC");
    System.out.println(userSoftReference.get());
    System.out.println("try to create byte array and Gc");

    byte[] b = new byte[1024 * 965 * 7];
    System.gc();
    System.out.println(userSoftReference.get());
    Thread.sleep(2000);
  }
}

在这里插入图片描述

弱引用

在系统进行GC时,只要发现弱引用,不管系统堆空间情况如何,都会将对象进行回收,但是,由于垃圾回收器的线程通常优先级很低,因此,并不一定能很快发现持有弱引用的对象,在这种情况下,弱引用对象可以存在较长的时间。一旦一个弱引用对象被来垃圾回收器回收,便会加入到一个注册的引用队列中

public class WeakRef {
  public static void main(String[] args) {
    SoftRefQ.User user = new SoftRefQ.User(1, "xxx");
    WeakReference<SoftRefQ.User> weakReference = new WeakReference<SoftRefQ.User>(user);
    user=null;
    System.out.println(weakReference.get());
    System.gc();
    System.out.println("After GC");
    System.out.println(weakReference.get());
  }
}

在这里插入图片描述

虚引用

一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被卡机回收器回收。当试图通过虚引用的get方法取得强引用时,总是失败。虚引用必须和引用队列一起使用,它的作用是跟踪来记回收过程。

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

public class TraceCanReliveObj {
  public static TraceCanReliveObj obj;
  static ReferenceQueue<TraceCanReliveObj> phantomQueue=null;
  public static class CheckRefQueue extends Thread{
    @Override
    public void run() {
      while(true){
        if (phantomQueue != null) {
          PhantomReference<TraceCanReliveObj> objt = null;
          try {
            objt = (PhantomReference<TraceCanReliveObj>)phantomQueue.remove();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }

          if (objt != null) {
            System.out.println("TraceCanReliveObj is delete by GC");
          }
        }
      }
    }
  }
  // 只有在第一次GC的时候会回调
  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("CanReliveObj finalize called");
    obj = this;
  }

  @Override
  public String toString() {
    return "TraceCanReliveObj{}";
  }

  public static void main(String[] args) throws InterruptedException{
    Thread t=new CheckRefQueue();
    t.setDaemon(true);
    t.start();

    phantomQueue = new ReferenceQueue<TraceCanReliveObj>();
    obj = new TraceCanReliveObj();
    PhantomReference<TraceCanReliveObj> phantomReference = new PhantomReference<TraceCanReliveObj>(obj,phantomQueue);
    obj = null;
    System.gc();
    Thread.sleep(1000);

    if (obj ==null) {
      System.out.println("obj is null");
    }else {
      System.out.println("obj可用");
    }
    System.out.println("第二次gc");
    obj = null;
    System.gc();
    Thread.sleep(1000);
    if (obj ==null) {
      System.out.println("obj is null");
    }else {
      System.out.println("obj可用");
    }
  }
}

第一次可用是在finalize中对象复活的,finalize函数只会被调用一次
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值