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()方法,該方法爲一個抽象方法,具體的實現交由具體的子類完成

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