此篇文字只简单阐述下java的四种引用类型和这四种引用存在的目的和应用场景
java的四种引用
引用类型
- 强引用
- 软引用 java.lang.ref.SoftReference
- 弱引用 java.lang.ref.WeakReference
- 虚引用 java.lang.ref.PhantomReference
强引用
在JDK1.1版本中,只有一种引用的概念(强引用):
TestBean testBeanReference=new TestBean();
上面的实例testBeanReference就是个引用变量,引用变量指向堆内存中的TestBean实例的内存地址。当java gc时回收内存会根据可达性分析(主流垃圾回收器)判断是否可以回收此块内存。如果没有把强引用变量testBeanReference=null gc不会回收内存。
软引用
但是有些场景直接使用强引用就不太方便,比如:
程序中需要保持一块数据在缓存中以提供系统吞吐量,但是缓存数据太大时会出现OOM异常导致系统宕机不可用。为了解决这种问题,开发者们提出了软引用(java.lang.ref.SoftReference),当内存充足时gc不会回收掉这种引用的对象内存,但是当内存不足时gc会回收调软引用对应的内存防止OOM异常。
//beanSoftReference 为软引用
SoftReference<Bean> beanSoftReference = new SoftReference<>(testBeanReference);
// 如果要到达内存不足时回收TestBean的上面的那个实例对应的内存 之前的强引用需要置为null
testBeanReference=null
System.gc();
弱引用
除了上面说的场景外,开发者们发现也会出现另外一种常用的场景无法方便的使用上面两种引用来解决。比如:
当有一个Map来存一些数据时,如果某一个key存的对象在使用过后只把强引用obj=null是无法回收调内存的。如果不想主动管理map中的key引用的释放,不调用remove方法。交给map自身处理就需要依赖弱引用(java.lang.ref.WeakReference)了。
HashMap<Object, Object> map = new HashMap<>();
//强引用obj
Object obj = new Object();
map.put(obj, "1");
//虽然obj被置为null 但是还是存在map中entry的key引用指向obj堆内存 导致此块内存无法被回收
//当时可以使用remove发放显性的把key置为null释放引用 map.remove(obj);
obj = null;
System.gc();
//给gc留点时间
Thread.sleep(500);
//WeakReference weakReference = new WeakReference(obj);
// 注意o1是强引用
// Object o1 = weakReference.get();
jdk1.2后新增java.util.WeakHashMap WeakHashMap的节点的key就是弱引用
使用下面的方式 当强引用o=null时gc后objectObjectWeakHashMap的节点的key也为null了。但是这样就会出现一个新的问题,集合map中会存在key=null但是value不为空的数据,如果不处理这样的数据也会造成内存泄露。答案就是WeakHashMap会在常用方法(get,put,size,resize等等)中增加了对key=null&&value!=null的数据删除。
WeakHashMap<Object, Object> objectObjectWeakHashMap = new WeakHashMap<>();
System.out.println("===");
Object o = new Object();
objectObjectWeakHashMap.put(o, 1);
o = null;
System.gc();
Thread.sleep(500);
objectObjectWeakHashMap.keySet().forEach(System.out::println);
另外多说一下,ThreadLocal也存在同样的问题,ThreadLocal中的ThreadLocalMap的节点Entry也是使用了弱引用。也同样在常用方法中处理了key=null&&value!=null的数据。所以要习惯性的使用remove方法,否则会造成内存泄露问题。
虚引用
在新增软引用,弱引用的前提下,开发者们又新增了虚引用java.lang.ref.PhantomReference,目的是能在这个对象被收集器回收时收到一个系统通知,必须配合ReferenceQueue队列使用。细心的人可以发现这个类的get方法返回的是null.所以这个引用偏重于定位和监听。比如定位某个对象是否被回收(是否属于内存泄露)或回收时间等。
总结
- 在软引用,弱引用,虚引用的构造方法中有一个ReferenceQueue队列,用于记录gc后回收释放引用的记录,开发者可以根据这个队列监控是否回收等事件。
- 软引用和内存是否充足有关
- 弱引用依赖强引用和软引用,只有这俩种引用都不存在时gc后方可释放。