java.lang.ref.* 包 提供了引用对象类,支持在某种程度上与垃圾回收器之间的交互。更准确的说是
通过引用对象类来窥探对象被垃圾回收器处理的状态。 程序可以使用一个引用对象来维持对另外某一对象的引用,但所采用的方式是使后者仍然可以被回收器在特定的场景下(内存不足,gc操作等)回收。同时程序还可以安排在回收器确定某一给定对象的可到达性已经更改之后的某个时间得到通知(初始化引用对象时 指定引用注册队列ReferenceQueue)。
垃圾回收器可以在特定的情景(或者说分阶段回收)下对对象进行回收。
其中的某些特定实现和应用系统的缓存设计是密切相关的,即通过这些对象引用来取对象,但是又保证内存不足的时候,释放内存,当然对象引用是不能获取到其关联的值了。,这也是本文重点关注的。
本文主要从使用和原理的角度来介绍相关概念。
引用的抽象接口
java.lang.ref.Reference
Reference:字面的含义就是引用,java中的引用 从垃圾回收分期回收的角度,又分为 强引用,软引用,弱引用,虚引用。
构造构造一个具体的Reference 可以传递 一个跟踪 Reference 的 ReferenceQueue ,当具体的Reference ,比如SoftReference 可以到达时,那么ReferenceQueue 中可以取得 这个引用,不可达时候,取不到。这种用法是垃圾回收器标记过此对象后,可以给ReferenceQueue 一个通知,即加入ReferenceQueue 队列,表示当前引用已经到达。这也是程序开发接口可以监控对象引用状态的点。
可达性概念
从最强到最弱,不同的可到达性级别反映了对象的生命周期。在操作上,可将它们定义如下:
如果某一线程可以不必遍历所有引用对象而直接到达一个对象,则该对象是强可到达 对象。新创建的对象对于创建它的线程而言是强可到达对象。
如果一个对象不是强可到达对象,但通过遍历某一软引用可以到达它,则该对象是软可到达 对象。
如果一个对象既不是强可到达对象,也不是软可到达对象,但通过遍历弱引用可以到达它,则该对象是弱可到达 对象。当清除对某一弱可到达对象的弱引用时,便可以终止此对象了。
如果一个对象既不是强可到达对象,也不是软可到达对象或弱可到达对象,它已经终止,并且某个虚引用在引用它,则该对象是虚可到达 对象。
最后,当不能以上述任何方法到达某一对象时,该对象是不可到达 对象,因此可以回收此对象。
下面是Reference 的几个实现
1、强引用,这个是我们普通使用的定义对象的方式。
java.lang.ref.FinalReference
eg:Object obj = new Object(); 那么obj对heap的Ojbect对象就是一个强引用。
特点:
强引用可以直接访问目标对象。
强引用所指向的对象在任何时候都不会被系统回收。
强引用可能导致内存泄漏。
2、软引用。软引用最主要的特点是对象会在内存不足的情况下才进行回收,所以常用来用作缓存的设计。
java.lang.ref.SoftReference
特点:
软引用使用 get() 方法取得对象的强引用从而访问目标对象。
软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。
软引用可以避免 Heap 内存不足所导致的异常
当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference 的 get() 方法将会返回 null,然后再调用对象的 finalize() 方法,并在下一轮 GC 中对其真正进行回收。
测试:
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
/**
* test SoftReference useage
* @author xinchun.wang
*/
public class SoftReferenceTest {
public static void main(String[] args) throws InterruptedException {
A a = new A();
a.src = "aaa";
ReferenceQueue<A> rq = new ReferenceQueue<A>();
SoftReference<A> weak = new SoftReference<A>(a, rq);
a = null;
System.out.println(weak.get().src); // exist
System.gc();
System.out.println(weak.get().src); // just gc,not use all heap , still exist
cleanHeapFunction(); //casue use all heap
System.out.println(weak.get()); // not exist
Reference<? extends A> rs = rq.poll(); //can get instance
System.out.println(rs);
System.out.println(rs.get());
}
/**
* use all heap ,casuse full gc
*/
public static void cleanHeapFunction(){
try{
StringBuffer bf = new StringBuffer();
for(int i=0;i<10000000000L;i++){
bf.append(i);
bf.append(bf);
}
}catch(Error e){
}
}
}
3、弱引用。弱引用最主要的特点是对象会在gc的时候立即回收,不考虑内存实际占用。所以在某些短生命周期的对象也是适合做缓存的。
java.lang.ref.WeakReference
特点:
弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
弱引用也可以避免 Heap 内存不足所导致的异常。
WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。
测试案例1:
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
class A {
public String src;
}
/**
* @author xinchun.wang */
public class WeakReferenceTest {
public static void main(String[] args) throws InterruptedException {
A a = new A();
a.src = "aaa";
ReferenceQueue<A> rq = new ReferenceQueue<A>();
WeakReference<A> weak = new WeakReference<A>(a, rq);
a = null; // make a = null ,not use this instance
System.out.println(weak.get().src); //we also get it,not remove from heap
System.gc(); //GC
Thread.sleep(1000);
Reference<? extends A> rs = rq.poll();
System.out.println(rs.get()); // rs.get() is null,remove from heap
}
}
测试案例2:下面这个程序永远不会抛内存溢出,原理同上。
package com.qunar.flight.tts.policy.client.validator.impl;
import java.util.WeakHashMap;
public class Test {
public static void main(String[] args) throws Exception {
WeakHashMap<byte[][], byte[][]> map = new WeakHashMap<byte[][], byte[][]>();
for (int i = 0; i < 10000000; i++) {
map.put(new byte[1000][1000], new byte[1000][1000]);
System.err.println(map.size());
}
}
}
4、虚引用。这个在实际使用中不太普遍。
java.lang.ref.PhantomReference
PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。
值得注意的是,对于引用回收方面,虚引用类似强引用不会自动根据内存情况自动对目标对象回收,Client 需要自己对其进行处理以防 Heap 内存不足异常。
应用举例:
在common-pool 2.x的版本中,提供了ObjectPool的一个实现为:SoftReferenceObjectPool
代码分析如下:
public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> {
/** 生成对象的工厂 */
private final PooledObjectFactory<T> factory;
/**
* 引用队列
*/
private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
/** 被pool的客户端取走的对象总量*/
private int numActive = 0; // @GuardedBy("this")
/** 销毁的所有实例的总数 */
private long destroyCount = 0;
/** 创建的实例的总数 */
private long createCount = 0;
/** 可以被客户端借用的空闲引用对象的引用队列 */
private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences =
new LinkedBlockingDeque<PooledSoftReference<T>>();
/** 已经被借走或者等待被借走的所有对象引用的引用队列 */
private final ArrayList<PooledSoftReference<T>> allReferences =
new ArrayList<PooledSoftReference<T>>();
/**
* 构造函数 指定对象创建工厂
*/
public SoftReferenceObjectPool(PooledObjectFactory<T> factory) {
this.factory = factory;
}
/**
* client 借用对象
*/
@Override
public synchronized T borrowObject() throws Exception {
assertOpen(); //保证pool 是打开的
T obj = null;
boolean newlyCreated = false;
PooledSoftReference<T> ref = null;
while (null == obj) {
if (idleReferences.isEmpty()) { //如果没有空闲的对象引用
if (null == factory) {
throw new NoSuchElementException(); //无法生产,直接抛出异常
} else {
newlyCreated = true;
obj = factory.makeObject().getObject();//工厂直接生产一个对象
createCount++;
// 包装成PooledSoftReference对象
ref = new PooledSoftReference<T>(new SoftReference<T>(obj));
allReferences.add(ref); //加入allReferences ,但是注意:不加入idleReferences!!!
}
} else { //如果有空闲的对象
ref = idleReferences.pollFirst(); //如果空闲有,从空闲里取出来
obj = ref.getObject(); //对象被强引用
ref.getReference().clear(); //清空对obj对象引用
ref.setReference(new SoftReference<T>(obj)); //这样会导致 allReferences 里的 ref 的SoftReference 清空,所以下面需要从新生成一个引用
}
if (null != factory && null != obj) {
try {
factory.activateObject(ref);
if (!factory.validateObject(ref)) {
throw new Exception("ValidateObject failed");
}
} catch (Throwable t) {
PoolUtils.checkRethrow(t);
try {
destroy(ref);
} catch (Throwable t2) {
PoolUtils.checkRethrow(t2);
} finally {
obj = null;
}
if (newlyCreated) {
throw new NoSuchElementException(
"Could not create a validated object, cause: " +t.getMessage());
}
}
}
}
numActive++; //激活数量+1
ref.allocate(); //设置对象的状态
return obj;
}
/**
* 归还一个对象
*/
@Override
public synchronized void returnObject(T obj) throws Exception {
boolean success = !isClosed();
final PooledSoftReference<T> ref = findReference(obj); //找到obj对应的对象引用
if (ref == null) {
throw new IllegalStateException("Returned object not currently part of this pool");
}
if (factory != null) {
if (!factory.validateObject(ref)) { //进行验证
success = false;
} else {
try {
factory.passivateObject(ref);
} catch (Exception e) {
success = false;
}
}
}
boolean shouldDestroy = !success;
numActive--; //归还后,numActive减少。
if (success) {
// Deallocate and add to the idle instance pool
ref.deallocate();//如果归还成功,那么该ref的状态
idleReferences.add(ref);//加入idleReferences 列表。
}
notifyAll(); // numActive has changed
if (shouldDestroy && factory != null) { //如果归还失败,那么销魂ref对象
try {
destroy(ref);
} catch (Exception e) {
// ignored
}
}
}
/**
* 让obj失效,即销毁对象
*/
@Override
public synchronized void invalidateObject(T obj) throws Exception {
final PooledSoftReference<T> ref = findReference(obj);
if (ref == null) {
throw new IllegalStateException("Object to invalidate is not currently part of this pool");
}
if (factory != null) {
destroy(ref);
}
numActive--;
notifyAll(); // numActive has changed
}
/**
这个方法主要用于 预加载情况下,加载对象到idle列表。
在加入列表前,对象进行 validateObject ,如果失败则:passivateObject。
*/
@Override
public synchronized void addObject() throws Exception {
assertOpen(); //确定池是打开的
if (factory == null) {
throw new IllegalStateException("Cannot add objects without a factory.");
}
T obj = factory.makeObject().getObject();
createCount++;
// Create and register with the queue
PooledSoftReference<T> ref = new PooledSoftReference<T>(
new SoftReference<T>(obj, refQueue)); //注意:注册此对象到 refQueue 列表。
allReferences.add(ref); //加入allReferences列表
boolean success = true;
if (!factory.validateObject(ref)) { //验证对象
success = false;
} else {
factory.passivateObject(ref); //验证失败进行
}
boolean shouldDestroy = !success;
if (success) { //如果成功才加入到空闲列表
idleReferences.add(ref);
notifyAll(); // numActive has changed
}
if (shouldDestroy) { //如果失败,那么idleReferences 和 idleReferences 进行清理ref对象
try {
destroy(ref);
} catch (Exception e) {
// ignored
}
}
}
/**
*返回一个idle对象的数量
*/
@Override
public synchronized int getNumIdle() {
pruneClearedReferences();
return idleReferences.size();
}
/**
*返回活动的(客户端在用的)数量
*/
@Override
public synchronized int getNumActive() {
return numActive;
}
/**
清空列表
*/
@Override
public synchronized void clear() {
if (null != factory) {
Iterator<PooledSoftReference<T>> iter = idleReferences.iterator();
while (iter.hasNext()) {
try {
final PooledSoftReference<T> ref = iter.next();
if (null != ref.getObject()) { //如果没有销毁,可能此对象已经被垃圾回收器回收!
factory.destroyObject(ref);
}
} catch (Exception e) {
}
}
}
idleReferences.clear();
pruneClearedReferences();
}
public void close() {
super.close();
clear();
}
public synchronized PooledObjectFactory<T> getFactory() {
return factory;
}
/**
* 如果任何对象被垃圾回收器回收,那么wrappers 必须清除其对应的PooledSoftReference 引用。
*/
private void pruneClearedReferences() {
// Remove wrappers for enqueued references from idle and allReferences lists
removeClearedReferences(idleReferences.iterator());
removeClearedReferences(allReferences.iterator());
while (refQueue.poll() != null) {
try {
_pool.remove(ref);
} catch (UnsupportedOperationException uoe) {
// ignored
}
}
}
private PooledSoftReference<T> findReference(T obj) {
Iterator<PooledSoftReference<T>> iterator = allReferences.iterator();
while (iterator.hasNext()) {
final PooledSoftReference<T> reference = iterator.next();
if (reference.getObject() != null && reference.getObject().equals(obj)) {
return reference;
}
}
return null;
}
/**
*
* 销毁一个对象 依赖于 生成此对象的工厂,另外把对象引用去除。
*/
private void destroy(PooledSoftReference<T> toDestroy) throws Exception {
toDestroy.invalidate();
idleReferences.remove(toDestroy);
allReferences.remove(toDestroy);
try {
factory.destroyObject(toDestroy);
} finally {
destroyCount++;
toDestroy.getReference().clear();
}
}
/**
* 清空已经被垃圾回收器 清除的对象的对象引用PooledSoftReference
*/
private void removeClearedReferences(Iterator<PooledSoftReference<T>> iterator) {
PooledSoftReference<T> ref;
while (iterator.hasNext()) {
ref = iterator.next();
if (ref.getReference() == null || ref.getReference().isEnqueued()) {
iterator.remove();
}
}
}
}