Netty源码(一)ByteBuf与引用计数

2,071 阅读15分钟

一、ByteBuf简介

下面的简介都来源于java doc。

ByteBuf是随机可访问且顺序可访问的字节序列。为byte数组和ByteBuffer提供抽象视图。

1、创建Buffer

推荐使用Unpooled,不要直接使用构造方法

2、随机访问

和普通原始字节数组一样,通过下标(0~容量)支持随机访问

3、顺序访问

通过readerIndex和writerIndex支持顺序访问 由readerIndex和writerIndex将buffer划分为三块:

  • discardable bytes:可丢弃的字节
  • readable bytes:可读的字节
  • writable bytes:可写的字节

相关方法:

  • readerIndex()/writerIndex():获取读写下标
  • readerIndex(int readerIndex)/writerIndex(int writerIndex):设置读写下标
  • discardReadBytes:丢弃discardable bytes
  • clear:重置读写index为0

4、搜索

  • 单字节搜索
    • indexOf(int, int, byte)
    • bytesBefore(int, int, byte)
    • bytesBefore(byte)
  • 复杂搜索
    • forEachByte(int, int, ByteProcessor)

5、标记和重置

读写index可以标记和重置

  • markReaderIndex()/markWriterIndex():标记当前index
  • resetReaderIndex()/resetWriterIndex():重置当前index为之前标记的index

6、衍生Buffer

  • 视图拷贝:底层数据存储是一份,只是复制一份视图,读写指针两个对象互不干涉
    • 无引用计数
      • duplicate() 不会修改当前ByteBuf的读写index
      • slice() 不会修改当前ByteBuf的读写index
      • slice(int, int) 不会修改当前ByteBuf的读写index
      • readSlice(int) 会修改当前ByteBuf读index
    • 有引用计数:相对于无引用计数,可能会产生更少垃圾,当引用计数为0时,直接内存会主动释放,使用堆内存会将字节数组置空
      • retainedDuplicate()
      • retainedSlice()
      • retainedSlice(int, int)
      • readRetainedSlice(int)
  • 完全拷贝:一个全新的ByteBuf,内容与老ByteBuf一致,底层数据存储是两份
    • copy()

7、JDK类型转换

  • 字节数组转换:hasArray()方法判断底层是否是字节数组支持,array()方法返回字节数组,对于直接内存创建的缓冲区hasArray()返回false
  • 字符串转换:toString(Charset)负责ByteBuf转换为String,注意toString()方法不是转换方法
  • IO流转换:ByteBufInputStream和ByteBufOutputStream

二、ByteBuf继承关系

这里重点挑选一些有代表性的接口和类做讨论。部分已废弃的接口和类不做讨论。

1、ReferenceCounted

A reference-counted object that requires explicit deallocation.

实现ReferenceCounted接口代表这个对象具有引用计数功能,但是需要显示释放对象。言外之意,这里只是定义了一个引用计数对象的计数器操作方法,实际释放对象占用资源,需要客户端自己显示调用。比如release减少计数到0,不一定是在release方法中做资源释放,需要客户端自己释放。

If the reference count is decreased to 0, the object will be deallocated explicitly, and accessing the deallocated object will usually result in an access violation

如果引用计数降至0,需要显示释放,并且使用释放后的对象会导致访问冲突

接口定义如下,这里省略了一些同样含义的快捷方法(如retain的无参方法)。

public interface ReferenceCounted {
    // 返回引用计数
    int refCnt();
    // 增加引用计数
    ReferenceCounted retain(int increment);
    // 减少引用计数
    boolean release(int decrement);
    // 记录当前实例,用以内存泄露分析,hint入参只是为了debug的额外信息
    ReferenceCounted touch(Object hint);
}

2、WrappedByteBuf

一个简单的包装类,所有ByteBuf的抽象方法,都委托实际包装的ByteBuf实现,代码省略。

class WrappedByteBuf extends ByteBuf {

    protected final ByteBuf buf;

    protected WrappedByteBuf(ByteBuf buf) {
        this.buf = ObjectUtil.checkNotNull(buf, "buf");
    }
}

3、EmptyByteBuf

空ByteBuf,大部分方法抛出IndexOutOfBoundsException,容量capcity和最大容量maxCapacity都为0。

部分代码如下。

public final class EmptyByteBuf extends ByteBuf {
	// 当前容量
    @Override
    public int capacity() {
        return 0;
    }

    // 扩容
    @Override
    public ByteBuf capacity(int newCapacity) {
        throw new ReadOnlyBufferException();
    }

    // 最大容量
    @Override
    public int maxCapacity() {
        return 0;
    }

    // 读
    @Override
    public byte getByte(int index) {
        throw new IndexOutOfBoundsException();
    }

    // 写
    @Override
    public ByteBuf writeByte(int value) {
        throw new IndexOutOfBoundsException();
    }
}

4、AbstractByteBuf

AbstractByteBuf是ByteBuf的骨架实现。

成员变量

注意到capacity当前容量,并不是AbstractByteBuf的成员变量,capacity方法获取当前容量延迟到了子类实现。

public abstract class AbstractByteBuf extends ByteBuf {
    // 是否校验引用计数为0时的ByteBuf访问,默认true
    static final boolean checkAccessible;
    // 是否做下标校验,如readIndex是否超过writeIndex,writeIndex是否超过capacity,默认true
    private static final boolean checkBounds;
    // 泄露检测
    static final ResourceLeakDetector<ByteBuf> leakDetector =
            ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);
    // 读下标
    int readerIndex;
    // 写下标
    int writerIndex;
    // 标记读下标
    private int markedReaderIndex;
    // 标记写下标
    private int markedWriterIndex;
    // 最大容量
    private int maxCapacity;
}

代表性方法

index相关操作

// 查询读index
@Override
public int readerIndex() {
    return readerIndex;
}
// 设置读index
@Override
public ByteBuf readerIndex(int readerIndex) {
	// 校验下标是否溢出
    if (checkBounds) {
        checkIndexBounds(readerIndex, writerIndex, capacity());
    }
    this.readerIndex = readerIndex;
    return this;
}
// 是否可读
@Override
public boolean isReadable() {
    return writerIndex > readerIndex;
}
// 可读字节
@Override
public int readableBytes() {
    return writerIndex - readerIndex;
}
// 清空读写下标
@Override
public ByteBuf clear() {
    readerIndex = writerIndex = 0;
    return this;
}
// 标记当前读index
@Override
public ByteBuf markReaderIndex() {
    markedReaderIndex = readerIndex;
    return this;
}
// 重置读index到标记位置
@Override
public ByteBuf resetReaderIndex() {
    readerIndex(markedReaderIndex);
    return this;
}
// 获取index位置的字节数据
@Override
public byte getByte(int index) {
	// 校验下标是否溢出
    checkIndex(index);
    // 子类实现实际逻辑
    return _getByte(index);
}
protected abstract byte _getByte(int index);
// 读取当前readIndex位置的字节
@Override
public byte readByte() {
    checkReadableBytes0(1);
    int i = readerIndex;
    // 子类实现
    byte b = _getByte(i);
    readerIndex = i + 1;
    return b;
}

创建衍生buffer

视图拷贝。注意这里duplicate和slice实例化了AbstractDerivedByteBuf (非池化衍生Buffer)的子类返回。

// 整个ByteBuf的视图拷贝,构造UnpooledDuplicatedByteBuf
@Override
public ByteBuf duplicate() {
    ensureAccessible();
    return new UnpooledDuplicatedByteBuf(this);
}
// 在duplicate的基础上调用retain增加引用计数
@Override
public ByteBuf retainedDuplicate() {
    return duplicate().retain();
}
// 切片视图拷贝,构造UnpooledSlicedByteBuf
@Override
public ByteBuf slice(int index, int length) {
    ensureAccessible();
    return new UnpooledSlicedByteBuf(this, index, length);
}
// 在slice的基础上调用retain增加引用计数
@Override
public ByteBuf retainedSlice(int index, int length) {
    return slice(index, length).retain();
}

复制

@Override
public ByteBuf copy() {
	// copy(int index, int length)方法由子类实现
    return copy(readerIndex, readableBytes());
}

JDK类型转换

AbstractByteBuf没有实现byte数组转换(array方法),只实现了字符串转换。

@Override
public String toString(Charset charset) {
    return toString(readerIndex, readableBytes(), charset);
}

@Override
public String toString(int index, int length, Charset charset) {
    return ByteBufUtil.decodeString(this, index, length, charset);
}

搜索

顺序搜索

@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
    if (fromIndex <= toIndex) {
        return firstIndexOf(fromIndex, toIndex, value);
    } else {
        return lastIndexOf(fromIndex, toIndex, value);
    }
}
// 简单的顺序搜索,找不到指定value返回-1
private int firstIndexOf(int fromIndex, int toIndex, byte value) {
    fromIndex = Math.max(fromIndex, 0);
    if (fromIndex >= toIndex || capacity() == 0) {
        return -1;
    }
    checkIndex(fromIndex, toIndex - fromIndex);

    for (int i = fromIndex; i < toIndex; i ++) {
        if (_getByte(i) == value) {
            return i;
        }
    }

    return -1;
}

复杂搜索

// index:起始下标
// length:搜索长度
// ByteProcessor:一个搜索处理器
@Override
public int forEachByte(int index, int length, ByteProcessor processor) {
    checkIndex(index, length);
    return forEachByteAsc0(index, index + length, processor);
}

int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
    for (; start < end; ++start) {
    	// 搜索处理器判断start位置的字节是否需要停止搜索
        if (!processor.process(_getByte(start))) {
            return start;
        }
    }
    return -1;
}

复杂搜索案例:

@Test
public void test02() {
    ByteBuf buffer = Unpooled.buffer(1024);
    buffer.writeByte('c').writeByte('a')
    .writeByte('d').writeByte(4).writeByte(' ').writeByte(5);
    // FIND_ASCII_SPACE搜索到空格时process方法返回false停止
    int i = buffer.forEachByte(ByteProcessor.FIND_ASCII_SPACE);
    System.out.println(i);// 下标为4
}

5、AbstractReferenceCountedByteBuf

AbstractReferenceCountedByteBuf实现了ReferenceCounted接口的所有方法,即引用计数。

成员变量

public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
    // refCnt字段的内存地址偏移量
    private static final long REFCNT_FIELD_OFFSET =
            ReferenceCountUpdater.getUnsafeOffset(AbstractReferenceCountedByteBuf.class, "refCnt");
    // refCnt字段的原子更新器
    private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> AIF_UPDATER =
            AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
    // 委托ReferenceCountUpdater实现ReferenceCounted
    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;
        }
    };
    // 初始虚拟引用计数为2
    private volatile int refCnt = updater.initialValue();
}

看到AbstractReferenceCountedByteBuf的成员变量主要是两个。

updater,ReferenceCountUpdater实例。ReferenceCounted接口的所有方法实现基本都是委托ReferenceCountUpdater实现原子更新的,只不过ReferenceCountUpdater是个抽象类,需要子类传入AtomicIntegerFieldUpdater原子更新器unsafeOffset引用计数字段偏移量

另一个是refCnt,记录了当前引用计数的虚拟值,这个虚拟值为2,实际引用次数是1。具体逻辑需要看ReferenceCountUpdater的实现。ReferenceCountUpdater每增加1个引用计数,refCnt会增加2。至于为什么不用实际引用次数累加,在后面讲。

关键方法

增加计数

@Override
public ByteBuf retain(int increment) {
    return updater.retain(this, increment);
}

减少计数

首先调用ReferenceCountUpdater对refCnt做原子更新。

@Override
public boolean release(int decrement) {
    return handleRelease(updater.release(this, decrement));
}

然后执行handleRelease方法,实际释放资源(回收当前ByteBuf),比如池化ByteBuf归还到池中,非池化ByteBuf直接释放,非池化HeapByteBuf把底层byte数组赋值为空数组,非池化DirectByteBuf把JDK的ByteBuffer释放。池化ByteBuf释放的逻辑会重点关注。

private boolean handleRelease(boolean result) {
    if (result) {
        deallocate();
    }
    return result;
}
/**
 * Called once {@link #refCnt()} is equals 0.
 */
protected abstract void deallocate();

获取计数

@Override
public int refCnt() {
    return updater.refCnt(this);
}

当前对象是否可访问

@Override
boolean isAccessible() {
    return updater.isLiveNonVolatile(this);
}

6、非池化Buffer

UnpooledHeapByteBuf

UnpooledHeapByteBuf是非池化的ByteBuf,底层使用字节数组存储数据

成员变量

public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
    // 分配器负责创建ByteBuf
    private final ByteBufAllocator alloc;
    // 底层字节数组
    byte[] array;
    // JDK ByteBuffer,用于适配JDK相关API
    // 底层还是会使用上面的array存储数据
    // 这里特意创建一个成员变量来存储是为了减少new对象次数
    private ByteBuffer tmpNioBuf;
}

核心方法

internalNioBuffer

部分JDK的API需要使用到原生的ByteBuffer,所以这里需要提供一个将byte数组封装为ByteBuffer的方法,且可以作为成员变量反复使用,减少new对象次数,同时也减少了垃圾回收次数。可以看到调用了ByteBuffer的wrap方法创建,底层存储数据还是用的成员变量array字节数组。

private ByteBuffer internalNioBuffer() {
    ByteBuffer tmpNioBuf = this.tmpNioBuf;
    if (tmpNioBuf == null) {
        this.tmpNioBuf = tmpNioBuf = ByteBuffer.wrap(array);
    }
    return tmpNioBuf;
}

setBytes

比如对于下面这个方法,需要将FileChannel写入当前UnpooledHeapByteBuf,但是没有api可以直接写入字节数组,所以要用到JDK的ByteBuffer帮助写入底层array。每次通过internalNioBuffer获取缓存ByteBuffer时,使用前都需要通过clear清空原始的各种下标。

@Override
public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
    return in.read((ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length), position);
}

对于下面这个方法,就不需要使用ByteBuffer,因为有api支持直接写入字节数组。

@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
    return in.read(array, index, length);
}

deallocate 因为UnpooledHeapByteBuf继承了AbstractReferenceCountedByteBuf,需要实现deallocate方法负责释放底层资源,这里将成员变量array这个字节数组置为空数组。

@Override
protected void deallocate() {
    array = EmptyArrays.EMPTY_BYTES;
}

UnpooledDirectByteBuf

UnpooledDirectByteBuf是非池化的ByteBuf,底层使用JDK的ByteBuffer存储数据,并且使用直接内存

成员变量

public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
    // 分配器 分配资源创建ByteBuf
    private final ByteBufAllocator alloc;
    // 底层是JDKByteBuffer来存储数据
    ByteBuffer buffer; // accessed by UnpooledUnsafeNoCleanerDirectByteBuf.reallocateDirect()
    // 一个临时的ByteBuffer,底层是由buffer.duplicate复制而来
    // 仅仅是为了减少垃圾回收,重复利用对象
    private ByteBuffer tmpNioBuf;
    // 当前容量 初始值为底层ByteBuffer的limit - position
    private int capacity;
    // 一个标志位,标记是否未释放ByteBuffer
    private boolean doNotFree;
}

构造方法

正常构造一个新的UnpooledDirectByteBuf。

public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
	// 设置最大容量
    super(maxCapacity);
    // 设置分配器
    this.alloc = alloc;
    // 设置this.buffer this.tmpNioBuf capacity
    setByteBuffer(allocateDirect(initialCapacity), false);
}

void setByteBuffer(ByteBuffer buffer, boolean tryFree) {
	// 尝试释放老的buffer直接内存,正常构造时用不到,只有扩容时会用到
    if (tryFree) {
        ByteBuffer oldBuffer = this.buffer;
        if (oldBuffer != null) {
            if (doNotFree) {
                doNotFree = false;
            } else {
            	// 释放直接内存
                // PlatformDependent.freeDirectBuffer(buffer)
                freeDirect(oldBuffer);
            }
        }
    }
    // 设置成员变量的初始值
    this.buffer = buffer;
    tmpNioBuf = null;
    // capacity = JDK buffer的limit - position
    capacity = buffer.remaining();
}

用已经存在的一个ByteBuffer作为底层buffer,创建UnpooledDirectByteBuf。

UnpooledDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer,
        int maxCapacity, boolean doFree, boolean slice) {
    super(maxCapacity);
    int initialCapacity = initialBuffer.remaining();
    this.alloc = alloc;
    doNotFree = !doFree;
    // slice为true则复制initialBuffer一份视图,保证指针独立,且position=0 limit和capacity为原来剩余字节数
    // 仍然调用setByteBuffer方法,初始化this.buffer this.tmpNioBuf capacity
    setByteBuffer((slice ? initialBuffer.slice() : initialBuffer).order(ByteOrder.BIG_ENDIAN), false);
    // 用initialBuffer.remaining(),即limit-position作为writeIndex
    writerIndex(initialCapacity);
}

核心方法

比较常用的读写buffer的方法就不放了,基本是操作数组和ByteBuffer的API。

internalNioBuffer

为了减少new对象次数,和垃圾回收次数,这里和UnpooledHeapByteBuf一样,会创建缓存的ByteBuffer,底层使用的ByteBuffer还是成员变量buffer。

// 复制一个ByteBuffer与buffer具有相同内容,但是index独立
// 初始index等同于原始buffer的index
private ByteBuffer internalNioBuffer() {
    ByteBuffer tmpNioBuf = this.tmpNioBuf;
    if (tmpNioBuf == null) {
        this.tmpNioBuf = tmpNioBuf = buffer.duplicate();
    }
    return tmpNioBuf;
}

deallocate

deallocate在实际引用计数归零时,会被父类触发,这里会设置成员变量buffer为null,但是UnpooledDirectByteBuf并不一定会直接释放底层ByteBuffer,是否释放取决于doNotFree标志位。当前buffer可能仍然在别的地方被使用,比如当前UnpooledDirectByteBuf是通过已经存在的一个ByteBuffer创建的(见第二个构造方法),可能当前buffer还不能被释放。

@Override
protected void deallocate() {
    ByteBuffer buffer = this.buffer;
    if (buffer == null) {
        return;
    }

    this.buffer = null;

    if (!doNotFree) {
        freeDirect(buffer);
    }
}

三、ReferenceCountUpdater

引用计数更新器,辅助实现ReferenceCounted接口的引用计数功能。先来看一下文档注释。

Implementation notes: 
For the updated int field:
	Even => "real" refcount is (refCnt >>> 1)
	Odd  => "real" refcount is 0

等待更新的int成员变量(refCnt),如果是偶数,实际引用次数=refCnt/2;如果是奇数,实际引用次数=0。为什么要用2的倍数来做计数,直接用实际的引用次数做计数不行吗?具体原因稍候给出。

(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) { ...

由于==操作比&操作性能更好,所以有些地方会有这样的实现,比如if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0)。相当于引用1到2次的对象可以通过==判断出来,大于2次或奇数要通过&判断出来。

初始引用次数

初始raw引用次数为2,代表有一个引用。

public final int initialValue() {
    return 2;
}

获取引用次数

获取实际引用次数,如果是偶数返回raw引用次数/2,如果是奇数返回0。

private static int realRefCnt(int rawCnt) {
    return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
}

获取实际引用次数,返回值同realRefCnt,只不过如果是奇数会抛出异常。

private static int toLiveRealRefCnt(int rawCnt, int decrement) {
    if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) {
        return rawCnt >>> 1;
    }
    throw new IllegalReferenceCountException(0, -decrement);
}

通过unsafe+offset,非volatile获取rawCnt。

private int nonVolatileRawCnt(T instance) {
    final long offset = unsafeOffset();
    return offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
}

通过unsafe+offset,volatile获取rawCnt,转换为realCnt。

public final int refCnt(T instance) {
    return realRefCnt(updater().get(instance));
}

通过unsafe+offset,非volatile获取rawCnt,返回对象是否仍然存在引用(rawCnt为偶数)。

public final boolean isLiveNonVolatile(T instance) {
	final long offset = unsafeOffset();
	final int rawCnt = offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);

	return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0;
}

增加引用次数

增加一次引用次数,在内部存储的时候会乘以2转换为rawCnt。

public final T retain(T instance, int increment) {
	// 实际累加值 = increment * 2
    int rawIncrement = checkPositive(increment, "increment") << 1;
    return retain0(instance, increment, rawIncrement);
}
private T retain0(T instance, final int increment, final int rawIncrement) {
	// 子类实现updater,原子累加
    int oldRef = updater().getAndAdd(instance, rawIncrement);
    // 如果老值不是2的倍数(偶数),表示实际引用次数为0,抛出异常
    if (oldRef != 2 && oldRef != 4 && (oldRef & 1) != 0) {
        throw new IllegalReferenceCountException(0, increment);
    }
    // getAndAdd超过Integer.MAX_VALUE溢出,需要回滚
    if ((oldRef <= 0 && oldRef + rawIncrement >= 0)
            || (oldRef >= 0 && oldRef + rawIncrement < oldRef)) {
        updater().getAndAdd(instance, -rawIncrement);
        throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
    }
    return instance;
}

减少引用次数

注意这里返回boolean值,代表对象是否实际引用计数为0,需要被回收。

public final boolean release(T instance, int decrement) {
	// 采用非volatile方式,获取当前raw引用次数
    int rawCnt = nonVolatileRawCnt(instance);
    // 获取实际引用次数
    int realCnt = toLiveRealRefCnt(rawCnt, checkPositive(decrement, "decrement"));
    return decrement == realCnt 
    		// 如果扣减引用次数等于实际引用次数,尝试设置rawCnt为奇数1,如果cas失败,无线循环CAS修改rawCnt
    		? tryFinalRelease0(instance, rawCnt) || retryRelease0(instance, decrement)
    		// 否则cas修改rawCnt = rawCnt - decrement * 2
            : nonFinalRelease0(instance, decrement, rawCnt, realCnt);
}

首先获取到当前的实际引用次数和raw引用次数,然后判断实际引用次数与待扣减次数是否相等。

如果相等,执行tryFinalRelease0方法。先尝试将raw引用次数改为1(任何奇数都认为该对象已被回收,这里写死的1)。

private boolean tryFinalRelease0(T instance, int expectRawCnt) {
    return updater().compareAndSet(instance, expectRawCnt, 1);
}

如果tryFinalRelease0方法CAS失败,无限循环执行CAS,区别是读取volatile的rawCnt,再次尝试修改raw引用次数。

private boolean retryRelease0(T instance, int decrement) {
	for (;;) {
	    // rawCnt读取是volatile读取,见AtomicIntegerFieldUpdaterImpl.get
	    int rawCnt = updater().get(instance), realCnt = toLiveRealRefCnt(rawCnt, decrement);
	    if (decrement == realCnt) {
	        // 如果扣减引用次数等于实际引用次数,尝试设置rawCnt为奇数1
	        if (tryFinalRelease0(instance, rawCnt)) {
	            // 返回true表示对象需要被回收
	            return true;
	        }
	    } else if (decrement < realCnt) {
	        // all changes to the raw count are 2x the "real" change
	        // 如果扣减引用次数小于实际引用次数,修改rawCnt = rawCnt - decrement * 2
	        if (updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
	            return false;
	        }
	    } else {
	        // 如果扣减引用次数大于实际引用次数,抛出异常
	        throw new IllegalReferenceCountException(realCnt, -decrement);
	    }
	    // 这有利于高竞争下的吞吐量
	    Thread.yield(); // this benefits throughput under high contention
	}
}

如果不相等,执行nonFinalRelease0方法,cas扣减raw引用次数,如果扣减成功返回false(表示当前对象不需要回收),否则一样执行上面的retryRelease0。

private boolean nonFinalRelease0(T instance, int decrement, int rawCnt, int realCnt) {
    // cas修改rawCnt = rawCnt - decrement * 2
    if (decrement < realCnt
            && updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
        return false;
    }
    return retryRelease0(instance, decrement);
}

为什么要用2的倍数作为引用计数?

因为某次性能优化,把retain0方法中的CAS修改引用计数,改为了getAndAdd,这样性能更好。但是随之而来的就是一个bug,当多线程操作同一个buffer的引用计数时,会发生问题。

考虑场景:一个object的refCnt为1。有3个线程修改这个object的引用计数。

  • 线程1调用obj.release(),修改refCnt为0,此时obj应该被进一步执行deallocate。
  • 线程2调用obj.retain(),修改refCnt为1,发现oldRef为0,然后去进行回滚getAndAdd(-1)并抛出异常。
  • 由于上一步的修改和回滚是两步操作,并不是原子的。所以在回滚前refCnt还是1,此时线程3调用obj.retain(),修改refCnt为2,oldRef为1,线程3认为这是一个存活的对象,并不会抛出异常
  • 线程1调用obj.deallocate(),释放了obj。

经过修复后,一个object的refCnt初始值为2,代表有一个引用。有3个线程修改这个object的引用计数。

  • 线程1调用obj.release(),修改refCnt为奇数,实现里是修改为1,此时obj应该被进一步执行deallocate。
  • 线程2调用obj.retain(),修改refCnt为3,发现oldRef为1是奇数,直接抛出异常。
  • 此时线程3调用obj.retain(),修改refCnt为5,发现oldRef为3是奇数,直接抛出异常。

参考github下面的三篇文章:

此外为什么是用2的倍数,不是3的倍数或者其他,原因应该是2的倍数用位运算方便。