AbstractReferenceCountedByteBuf
作用
AbstractReferenceCountedByteBuf类提供了引用计数的功能,其所有的子类都可以使用该功能防止内存泄漏。
属性
REFCNT_FIELD_OFFSET:refCnt字段在内存中的地址偏移量
AIF_UPDATER:refCnt字段更新器
updater:更新器
refCnt:保存引用计数的字段
private static final long REFCNT_FIELD_OFFSET =
ReferenceCountUpdater.getUnsafeOffset(AbstractReferenceCountedByteBuf.class, "refCnt");
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> AIF_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
private static final ReferenceCountUpdater<AbstractReferenceCountedByteBuf> updater =
new ReferenceCountUpdater<AbstractReferenceCountedByteBuf>() {
@Override
protected AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater() {
return AIF_UPDATER;
}
@Override
protected long unsafeOffset() {
return REFCNT_FIELD_OFFSET;
}
};
@SuppressWarnings("unused")
private volatile int refCnt = updater.initialValue();
由initialValue()方法的实现可知refCnt的初始值为2
public final int initialValue() {
return 2;
}
关于AtomicIntegerFieldUpdater类和ReferenceCountUpdater类
ReferenceCountUpdater类的部分方法如下所示,updater()和unsafeOffset()方法是抽象方法,需要具体的子类提供实现。而AtomicIntegerFieldUpdater是JDK提供的一个可以通过原子更新的方式修改指定字段的工具。
故ReferenceCountUpdater类的功能是可以通过CAS的方式直接修改某个类的一个字段的值。updater()方法用于提供修改字段值的工具类,unsafeOffset()方法用于提供要修改的字段在内存中的地址偏移量。
为什么ReferenceCountUpdater中持有的refCnt的值是偶数?
refCnt是偶数则表示当前缓冲区的状态为正常状态,如果refCnt是奇数则表示缓冲区的状态为待销毁状态。缓冲区引用计数的真实值为refCnt/2。
构造函数:
调用父类(AbstractByteBuf)的构造方法设置ByteBuf的最大容量:
protected AbstractReferenceCountedByteBuf(int maxCapacity) {
super(maxCapacity);
}
获取引用计数:
@Override
public int refCnt() {
return updater.refCnt(this);
}
updater的refCnt方法如下:
public final int refCnt(T instance) {
return realRefCnt(updater().get(instance));
}
updater().get(instance)的作用是获取instance对象指定字段的值(为真实值的2倍),再调用realRefCnt(int rawCnt)方法计算引用的真实值。
realRefCnt(int rawCnt)方法如下,
/**
* 判断rawCnt的值是否为偶数,如果rawCnt是偶数则返回rawCnt无符号右移1位的结果
* 如果rawCnt为奇数,则返回0
*/
private static int realRefCnt(int rawCnt) {
return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
}
增加引用计数的值:
@Override
public ByteBuf retain() {
return updater.retain(this);
}
retain()方法委派updater类对refCnt进行增加,updater#retain(T instance)如下所示:
public final T retain(T instance) {
return retain0(instance, 1, 2);
}
updater#retain0(T instance, final int increment, final int rawIncrement)方法如下所示:
/**
* 先执行操作,再进行校验
*/
private T retain0(T instance, final int increment, final int rawIncrement) {
int oldRef = updater().getAndAdd(instance, rawIncrement);//CAS,先获取原值,再增加
//如果oldRef为奇数,表示缓冲区已经被释放,则抛出异常
if (oldRef != 2 && oldRef != 4 && (oldRef & 1) != 0) {
throw new IllegalReferenceCountException(0, increment);
}
/**
* 如果对oldRef增加后发生了溢出,即超出了Integer.MAX_VALUE的值,则对其进行回滚,并抛出异常
*/
if ((oldRef <= 0 && oldRef + rawIncrement >= 0)
|| (oldRef >= 0 && oldRef + rawIncrement < oldRef)) {
// overflow case
updater().getAndAdd(instance, -rawIncrement);
throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
}
return instance;
}
减少引用计数的值
@Override
public boolean release() {
return handleRelease(updater.release(this));
}
ReferenceCountUpdater#release(T instance)如下所示:
public final boolean release(T instance) {
int rawCnt = nonVolatileRawCnt(instance);//通过Unsafe的方式获取引用计数的值
return rawCnt == 2 ? tryFinalRelease0(instance, 2) || retryRelease0(instance, 1)
: nonFinalRelease0(instance, 1, rawCnt, toLiveRealRefCnt(rawCnt, 1));
}
ReferenceCountUpdater#tryFinalRelease0(T instance, int expectRawCnt)方法:
通过cas的方式将引用计数的值修改为1
private boolean tryFinalRelease0(T instance, int expectRawCnt) {
return updater().compareAndSet(instance, expectRawCnt, 1); // any odd number will work
}
ReferenceCountUpdater#retryRelease0(T instance, int decrement):\
代码逻辑如下:
1、获取instance实例中保存的rawCnt值,并计算出真实的refCnt值,即realCnt
2、如果要减少的引用值和真实的refCnt值相同,也即需要释放缓冲区对象,则调用tryFinalRelease0方法将refCnt
的数值修改为1(只要修改为奇数即可)
3、如果要减少的引用值小于真实的refCnt值,则通过cas修改refCnt的值
4、调用Thread.yield()方法释放出CPU的执行权,因为修改引用计数的逻辑在整个系统逻辑的优先级并不高,所以让出执行权有利于提高高并发下的系统吞吐量
/**
* 自旋的方式通过cas修改引用计数的值
*
* rawCnt:instance对象中保存的值 realCnt:经过计算后得到的值(如果是偶数则除以2)
*/
private boolean retryRelease0(T instance, int decrement) {
for (;;) {
int rawCnt = updater().get(instance), realCnt = toLiveRealRefCnt(rawCnt, decrement);
if (decrement == realCnt) {
if (tryFinalRelease0(instance, rawCnt)) {
return true;
}
} else if (decrement < realCnt) {
// all changes to the raw count are 2x the "real" change
if (updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
return false;
}
} else {
throw new IllegalReferenceCountException(realCnt, -decrement);
}
Thread.yield();
}
}
AbstractReferenceCountedByteBuf#handleRelease(boolean result)方法
private boolean handleRelease(boolean result) {
if (result) {
deallocate();
}
return result;
}
handleRelease方法调用deallocate()方法,该方法为一个抽象方法,具体的实现交由具体的子类完成