之前介绍ByteBuf基本API及相关实现类时,不知道大家有没有注意到,ByteBuf抽象类实现了ReferenceCounted接口。这个接口是做什么的呢?主要是做计数用的。为什么需要计数呢?我们暂且不表,先分析下这个接口的若干方法如何实现计数功能,最后再说明下计数使用的场景。
1.ReferenceCounted接口的基本方法public interface ReferenceCounted {
/**
* Returns the reference count of this object. If {@code 0}, it means this object has been deallocated.
* 获取对象的引用数
*/
int refCnt();
/**
* Increases the reference count by {@code 1}.
* 将对象引用数+1
*/
ReferenceCounted retain();
/**
* Increases the reference count by the specified {@code increment}.
* 将对象引用数+increment个
*/
ReferenceCounted retain(int increment);
/**
* Records the current access location of this object for debugging purposes.
* If this object is determined to be leaked, the information recorded by this operation will be provided to you
* via {@link ResourceLeakDetector}. This method is a shortcut to {@link #touch(Object) touch(null)}.
*/
ReferenceCounted touch();
/**
* Records the current access location of this object with an additional arbitrary information for debugging
* purposes. If this object is determined to be leaked, the information recorded by this operation will be
* provided to you via {@link ResourceLeakDetector}.
*/
ReferenceCounted touch(Object hint);
/**
* Decreases the reference count by {@code 1} and deallocates this object if the reference count reaches at
* {@code 0}.
*
* 将对象引用数-1
*/
boolean release();
/**
* Decreases the reference count by the specified {@code decrement} and deallocates this object if the reference
* count reaches at {@code 0}.
* 将对象引用数-decrement个,当对象引用数为0时,代表当前对象可被释放
* @return {@code true} if and only if the reference count became {@code 0} and this object has been deallocated
*/
boolean release(int decrement);
}
英文注释还是比较给力的,通过注释我们可以了解到每个方法的含义。就是这个touch()方法不太好理解,没关系,后续看看能不能通过代码来理解到。
2.寻找ReferenceCounted接口实现类一头雾水的时候,我们先去寻找接口的实现类,在ByteBuf中,我们没有找到,那么继续寻找其子类实现,终于在AbstractReferenceCountedByteBuf中我们找到了。
具体看下其实现
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
private static final long REFCNT_FIELD_OFFSET =
ReferenceCountUpdater.getUnsafeOffset(AbstractReferenceCountedByteBuf.class, "refCnt");
private static final AtomicIntegerFieldUpdater AIF_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
// ReferenceCountUpdater作为更新器,计数器的主要功能都交由该类来实现
private static final ReferenceCountUpdater updater =
new ReferenceCountUpdater() {
protected AtomicIntegerFieldUpdater updater() {
return AIF_UPDATER;
}
@Override
protected long unsafeOffset() {
return REFCNT_FIELD_OFFSET;
}
};
// 计数信息
private volatile int refCnt = updater.initialValue();
// 获取计数器
public int refCnt() {
return updater.refCnt(this);
}
// 增加计数
public ByteBuf retain() {
return updater.retain(this);
}
// 减少计数
public boolean release() {
return handleRelease(updater.release(this));
}
private boolean handleRelease(boolean result) {
if (result) {
deallocate();
}
return result;
}
}
通过之前ByteBuf基本API的分析中,我们知道,AbstractReferenceCountedByteBuf作为一个公共子类,基本所有重要的ByteBuf实现类都继承了该子类。那么可以理解为所有的ByteBuf都有计数器功能。
既然功能都交由ReferenceCountUpdater来实现了,那么我们来分析下该类。
3.ReferenceCountUpdaterpublic abstract class ReferenceCountUpdater {
/*
* Implementation notes:
*
* For the updated int field:
* Even => "real" refcount is (refCnt >>> 1)
* Odd => "real" refcount is 0
*
* (x & y) appears to be surprisingly expensive relative to (x == y). Thus this class uses
* a fast-path in some places for most common low values when checking for live (even) refcounts,
* for example: if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) { ...
*/
protected ReferenceCountUpdater() { }
protected abstract AtomicIntegerFieldUpdater updater();
public final int refCnt(T instance) {
return realRefCnt(updater().get(instance));
}
...
}
还是一脸懵逼,除了留下一堆奇怪的注释,就是把功能交由AtomicIntegerFieldUpdater来实现了。
回过头来看,这里的AtomicIntegerFieldUpdater 就是 AbstractReferenceCountedByteBuf.AIF_UPDATER属性。没办法,回过头先学习AIF_UPDATER吧。
4.AIF_UPDATERprivate static final AtomicIntegerFieldUpdater AIF_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
第一次见到AtomicIntegerFieldUpdater,AtomicInteger笔者倒是用过,用来进行Integer的原子操作。那AtomicIntegerFieldUpdater呢?具体的笔者不再赘述,大家可以参考下 笑谈java并发编程四之AtomicIntegerFieldUpdater介绍_我要上天-CSDN博客 这篇文章,里面有其使用示例。
总体来说,就是通过AIF_UPDATER,可以实现对AbstractReferenceCountedByteBuf.refCnt属性(也就是对象引用计数器)的原子加减操作。
Q:那么问题来呢?为什么不直接将refCnt属性设置为AtomicInteger呢?
A:因为ByteBuf在Netty中使用的很多,如果这么多的对象在计数时都使用AtomicInteger而不是int基本类型,那么则是一笔很大的开销。所以为了Netty为了性能,使用全局的AIF_UPDATER来对refCnt这个基本属性进行原子操作。
好了,分析完AIF_UPDATER之后,我们可以回到正题了,继续对ReferenceCountUpdater的基本方法进行分析
5.ReferenceCountUpdater的APIpublic abstract class ReferenceCountUpdater {
/*
* Implementation notes:
*
* For the updated int field:
* Even => "real" refcount is (refCnt >>> 1)
* Odd => "real" refcount is 0
*
* (x & y) appears to be surprisingly expensive relative to (x == y). Thus this class uses
* a fast-path in some places for most common low values when checking for live (even) refcounts,
* for example: if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) { ...
*/
public final int initialValue() {
return 2;
}
// 获取对象真实引用值
private static int realRefCnt(int rawCnt) {
// 若rawCnt为奇数,则直接返回0
return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
}
// 增加计数
public final T retain(T instance) {
return retain0(instance, 1, 2);
}
public final T retain(T instance, int increment) {
// all changes to the raw count are 2x the "real" change - overflow is OK
int rawIncrement = checkPositive(increment, "increment") = 0 && oldRef + rawIncrement < oldRef)) {
// overflow case
updater().getAndAdd(instance, -rawIncrement);
throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
}
return instance;
}
// 减少计数
public final boolean release(T instance) {
// 获取引用计数
int rawCnt = nonVolatileRawCnt(instance);
// 若是2,则说明只有一个引用,那么再减少一次的话,就可以彻底释放instance
// 若非2,则说明还有多次引用,那么就正常减少一次
return rawCnt == 2 ? tryFinalRelease0(instance, 2) || retryRelease0(instance, 1)
: nonFinalRelease0(instance, 1, rawCnt, toLiveRealRefCnt(rawCnt, 1));
}
// 将引用值设置为1,奇数说明当前instance无引用,后续可被释放
private boolean tryFinalRelease0(T instance, int expectRawCnt) {
return updater().compareAndSet(instance, expectRawCnt, 1); // any odd number will work
}
private boolean nonFinalRelease0(T instance, int decrement, int rawCnt, int realCnt) {
// 直接将rawCnt-2
if (decrement < realCnt
// all changes to the raw count are 2x the "real" change - overflow is OK
&& updater().compareAndSet(instance, rawCnt, rawCnt - (decrement >> 1,比如若rawCnt=8,则说明真实引用值为8 >>> 1 = 4。
所以,对ReferenceCountUpdater而言,每增加一次引用,则计数器+2,每减少一次引用,则计数器-2,若最终引用为2时,则设置为1,说明当前对象已经没有被引用了。
6.计数器被调用的那些场景
上面讲述了那么多,那么计数器是在什么时候被调用的呢?什么时候对计数器进行加减操作的呢,貌似没有手动调用过。那么我们可以先通过一个示例手动调用下。
6.1 手动进行ByteBuf的引用与释放
ByteBuf buf = Unpooled.directBuffer(512);
System.out.println(buf);
try {
// 业务操作,将数据存入buf
} finally {
// 最终业务完成,将buf释放
buf.release();
}
示例比较简单,我们获取一个DirectByteBuf(非池化的),在一系列的业务操作后,最终将使用完成的ByteBuf释放掉(调用release方法)。
释放方法在release中,我们来看下这个方法的操作
6.1.1 ByteBuf.release()
最终实现在其子类AbstractReferenceCountedByteBuf中
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
public boolean release(int decrement) {
// 使用上面分析的 updater 来进行当前对象的引用数减少
// 若引用数比较多,则updater.release()返回false,因为还有其他引用;否则返回true,意味着所有的引用都已经被释放
return handleRelease(updater.release(this, decrement));
}
private boolean handleRelease(boolean result) {
// 若当前实例没有被引用了,则直接调用deallocate进行内存释放
if (result) {
// 在具体子类中实现,笔者不再赘述
deallocate();
}
return result;
}
}
上面的示例是我们手动调用的释放,那么在实际的应用中这个ByteBuf是如何释放的呢?
这个不是本文的重点,笔者可以提示下,就是在SimpleChannelInboundHandler.channelRead()方法中
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
// 在这里进行释放
// 最终调用((ReferenceCounted) msg).release()减少计数
ReferenceCountUtil.release(msg);
}
}
}
总结:
本文详细介绍了ByteBuf的引用计数器的实现,也学到了一些之前没有接触过的知识点(比如AtomicIntegerFieldUpdater)。算是大体上对引用计数有一定的了解了。
那么问题来了,费了这么大功夫了解引用计数,最终是做什么用的呢?暂且不表,下篇博客中来介绍。