淡抹宜人妆

导航

JAVA中的四大引用

强引用(StrongReference)

我们平常使用new操作符来创建的对象就是强引用对象,只要有一个引用存在,垃圾回收器永远不可能回收具有强引用的对象。

Object obj=new Object();

 

注意:

强引用的对象并不是永远不会被回收,需要把obj值为null,或者超出对象的生命周期之后,GC就有机会去回收它,具体什么时候回收要看GC。还有,这里的StrongReference只是一个对强引用的称呼,在java中并没有对应的实体类。

软引用(SoftReference)

软引用是用来描述一些还有用但并非必须的对象。当内存充足时,垃圾回收器不会清理具有软引用的对象,只有当内存不足时垃圾回收器才会去清理这些对象,如果清理完软引用的对象后内存还是不足才会抛出异常。

软引用在java中也是一个对象,对应的实体类是SoftReference

 

案例:

这个案例我们事先把最大堆内存改为了24M

-Xmx24M

 

/**
  * 软引用demo
  * SoftReference
  * 1.当内存不足的时,JVM就会把软引用对象进行回收
  * 2.如果回收后还是没有足够的内存,才会抛出内存溢出异常
  */
public static void main(String[] args) throws InterruptedException {
 SoftReference<byte[]> s=new SoftReference<>(new         byte[1024*1024*10]);//10m
 System.out.println(s.get());
 System.gc();//启动GC
 Thread.sleep(500);
 System.out.println(s.get());
 //再创建一个数组,堆中存不下的时候,垃圾回收器工作
 //先回收一次,如果第一次回收后内存还是不够
 //则再清理第二次,这一次会把软引用对象清除
 byte[] b=new byte[1024*1024*15];//15m
 System.out.println(s.get());//null
}

控制台打印结果:

[B@2a139a55
[B@2a139a55
null

此外,还可以通过以下JVM参数来打印GC日志

-XX:+PrintGC //打印简单的GC日志
-XX:+PrintGCDetails //打印详细的GC日志

 

通过控制台的打印结果我们得出结论:内存充足的情况下,具有软引用的对象不会被垃圾回收器回收,当再次创建了新的对象,结果导致堆内存不足时就会启动第一次GC,这一次不会回收软引用关联的对象,但是当第一次清理之后发现内存还是不够,则会再启动第二次GC,这一次GC才会清理掉软引用关联的对象。

 

由于,在JAVA中软引用也是一个类,我们需要软引用需要创建软引用类实例,我们在上面案例中,变量s的引用指向的是new SoftReference()这个实例对象,属于强引用关系,而在这个实例对象的里面又去引用了我们new出来的byte数组实例,这个引用是软引用关系。

SoftReference<byte[]> s=new SoftReference<>(new byte[1024*1024*10]);

关系图如下:

 

软引用非常适合用在缓存中,假如用户访问的系统中需要加载很多图片,内存够用的时候可以缓存很多图片,假如内存不够用了,再把图片先回收掉也无妨,下次需要的时候再加载一次即可。

弱引用(WeakReference)

无论内存够不够,只要垃圾回收器启动,弱引用关联的对象肯定被回收。

弱引用对象的实体类是WeakReference

案例:
/**
  * 弱引用demo
  * WeakReference
  * 不管内存够不够,都会进行回收
  */
public static void main(String[] args) {
 WeakReference<Object> w=new WeakReference<Object>(new Object());
 System.out.println(w.get());
 System.gc();
 System.out.println(w.get());
}

控制台打印结果

java.lang.Object@2a139a55
null

可以看出,弱引用关联的对象只能存活到下一次启动GC之前。

弱引用可以用来解决内存泄露的问题,比如:ThreadLocal中的key就使用到了弱引用来防止内存泄露。

关系图如下:

 

转载:ThreadLocal源码分析

虚引用(PhantomReference)

虚引用,又称作幻象引用,如果一个对象具有虚引用,那么它和没有任何引用一样,被虚引用关联的对象引用通过get方法获取到的永远为null,也就是说这种对象在任何时候都有可能被垃圾回收器回收,通过这种方式关联的对象也无法调用对象中的方法。虚引用主要是用来管理堆外内存的,通过ReferenceQueue这个类实现,当一个对象被回收的时候,会向这个引用队列里面添加相关数据,给一个通知。

案例一:
Object obj=new Object();
 PhantomReference<Object> objRef=new PhantomReference<Object>(obj,null);
 System.out.println("获取虚引用所指向的对象"+objRef.get());
 System.out.println(objRef.get().equals(obj));//尝试调用对象中的方法

控制台打印结果:

获取虚引用所指向的对象null
Exception in thread "main" java.lang.NullPointerException
 at com.sy.Reference.Test_phantom.main(Test_phantom.java:14)

关系图如下:

 

虚引用配合ReferenceQueue类,可以用来管理堆外内存,如果虚引用对象被回收后,会向引用队列里面发送一个通知,可以参考以下demo便于理解。

案例二:
/**
 * 虚引用
 *  管理堆外内存
 */
public class Test_PhantomReference {
//引用队列
private static final ReferenceQueue<Object> QUEUE=new ReferenceQueue<>();
public static void main(String[] args) {
 //当虚引用对象被回收时,会把一个信息填入到引用队列中
 PhantomReference<Object> p=new PhantomReference<Object>(new Object(),QUEUE);
 System.out.println("第一次获取虚引用指示的对象"+p.get());//null
 System.out.println("第一次获取虚引用的地址值"+p);
 List<byte[]> list=new ArrayList<>();
 new Thread(()->{
  boolean flag=true;
  try {
   while(flag) {
    //不断去new新的对象,内存不足时GC就会启动
    list.add(new byte[1024*1024]); 
   }
  } catch (Exception e) {
   e.printStackTrace();
  }finally {
   flag=false;
   System.out.println("第二次获取虚引用指示的对象"+p.get());
  }
 }).start();
 /* 再开启一个线程,做一个监控
  * 当虚引用被回收时,会发送一个通知
  * 如果引用队列QUEUE中不再是null
  * 证明虚引用已经被回收
  */
 new Thread(()->{
  boolean flag=true;
  while(flag) {
   Reference<? extends Object> poll = QUEUE.poll();
   if(poll!=null) {
    flag=false;
    System.out.println("虚引用对象"+poll+"被回收了");
   }
  }
 }).start();
}
}

虚引用可以用来管理堆外内存,以上案例中我们结合了一个Queue来进行测试,开启一个线程来进行监控,假如虚引用对象被回收那么通过poll方法就可以得知。

posted on 2022-02-22 18:51  淡抹宜人妆  阅读(54)  评论(0编辑  收藏  举报