netty AbstractReferenceCountedByteBuf类

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()方法,该方法为一个抽象方法,具体的实现交由具体的子类完成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章