共计 19832 个字符,预计需要花费 50 分钟才能阅读完成。
欢迎关注公众号:【爱编码 】
如果有需要后台回复 2019 赠送 1T 的学习资料 哦!!
简介
所有的网路通信都涉及字节序列的移动,所以高效易用的数据结构明显是必不可少的。Netty 的 ByteBuf 实现满足并超越了这些需求。
ByteBuf 结构
ByteBuf 维护了两个不同的索引:一个是用于读取,一个用于写入 。当你从 ByteBuf 读取是,它的readerIndex
将会被递增已经被读取的字节数。同样地,当你写入 ByteBuf 时,它的 witerIndex
也会被递增。
作为一个容器,源码中的如下。有三块区域
discardable bytes:无效空间(已经读取过的空间),可丢弃字节的区域,由readerIndex 指针 控制
readable bytes:内容空间,可读字节的区域, 由 readerIndex 和 writerIndex 指针控制控制
writable bytes:空闲空间,可写入字节的区域, 由 writerIndex 指针和 capacity 容量控制
* <pre>
* +-------------------+------------------+------------------+
* | discardable bytes | readable bytes | writable bytes |
* | | (CONTENT) | |
* +-------------------+------------------+------------------+
* | | | |
* 0 <= readerIndex <= writerIndex <= capacity
* </pre>
ByteBuf 使用模式
总体分类划分是可根据 JVM 堆内存来区分的。
1. 堆内内存(JVM 堆空间内)
2. 堆外内存(本机直接内存)
3. 复合缓冲区(以上 2 种缓冲区多个混合)
1. 堆内内存
最常用的 ByteBuf 模式是将 数据存储在 JVM 的堆空间中。它能在没有使用池化的情况下提供快速的分配和释放。
2. 堆外内存
JDK 允许 JVM 实现通过本地调用来分配内存。主要是为了避免每次调用本地 I / O 操作之前(或者之后)将缓冲区的内容复制到一个中间缓冲区(或者从中间缓冲区把内容复制到缓冲区)。
** 最大的特点: 它的内容将驻留在常规的会被垃圾回收的堆之外。
最大的缺点:相对于堆缓冲区,它的分配和释放都是较为昂贵的。**
3. 复合缓冲区
常用类:CompositeByteBuf,它为多个 ByteBuf 提供一个聚合视图,将多个缓冲区表示为单个合并缓冲区的虚拟表示。
比如:HTTP 协议:头部和主体这两部分由应用程序的不同模块产生。这个时候把这两部分合并的话,选择 CompositeByteBuf 是比较好的。
ByteBuf 分类
主要分为三大类
Pooled 和 Unpooled(池化)
unsafe 和非 unsafe()
Heap 和 Direct(堆内和堆外)
Pooled 和 Unpooled
Pooled:每次都从预先分配好的内存中去取出一段连续内存封装成一个 ByteBuf 给应用程序使用
Unpooled:每次分配内存的时候,直接调用系统 api,向操作系统申请一
块内存
Heap 和 Direct:
Head:是调用 jvm 的堆内存进行分配的,需要被 gc 进行管理
Direct:是调用 jdk 的 api 进行内存分配,不受 jvm 控制,不会参与到 gc 的过程
Unsafe 和非 Unsafe
jdk 中有 Unsafe 对象可以直接拿到对象的内存地址,并且基于这个内存地址进行读写操作。那么对应的分类的区别就是是否可以拿到 jdk 底层的 Unsafe 进行读写操作了。
Java 为什么会引入及如何使用 Unsafe
内存分配 ByteBufAllocator
这个接口实现负责分配缓冲区并且是线程安全的。从下面的接口方法以及注释可以总结出主要是围绕上面的三种 ByteBuf 内存模式:堆内,堆外以及复合型的内存分配。
/**
* Implementations are responsible to allocate buffers. Implementations of this interface are expected to be
* thread-safe.
*/
public interface ByteBufAllocator {
ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;
/**
* Allocate a {@link ByteBuf}. If it is a direct or heap buffer
* depends on the actual implementation.
*/
ByteBuf buffer();
/**
* Allocate a {@link ByteBuf} with the given initial capacity.
* If it is a direct or heap buffer depends on the actual implementation.
*/
ByteBuf buffer(int initialCapacity);
/**
* Allocate a {@link ByteBuf} with the given initial capacity and the given
* maximal capacity. If it is a direct or heap buffer depends on the actual
* implementation.
*/
ByteBuf buffer(int initialCapacity, int maxCapacity);
/**
* Allocate a {@link ByteBuf}, preferably a direct buffer which is suitable for I/O.
*/
ByteBuf ioBuffer();
/**
* Allocate a {@link ByteBuf}, preferably a direct buffer which is suitable for I/O.
*/
ByteBuf ioBuffer(int initialCapacity);
/**
* Allocate a {@link ByteBuf}, preferably a direct buffer which is suitable for I/O.
*/
ByteBuf ioBuffer(int initialCapacity, int maxCapacity);
/**
* Allocate a heap {@link ByteBuf}.
*/
ByteBuf heapBuffer();
/**
* Allocate a heap {@link ByteBuf} with the given initial capacity.
*/
ByteBuf heapBuffer(int initialCapacity);
/**
* Allocate a heap {@link ByteBuf} with the given initial capacity and the given
* maximal capacity.
*/
ByteBuf heapBuffer(int initialCapacity, int maxCapacity);
/**
* Allocate a direct {@link ByteBuf}.
*/
ByteBuf directBuffer();
/**
* Allocate a direct {@link ByteBuf} with the given initial capacity.
*/
ByteBuf directBuffer(int initialCapacity);
/**
* Allocate a direct {@link ByteBuf} with the given initial capacity and the given
* maximal capacity.
*/
ByteBuf directBuffer(int initialCapacity, int maxCapacity);
/**
* Allocate a {@link CompositeByteBuf}.
* If it is a direct or heap buffer depends on the actual implementation.
*/
CompositeByteBuf compositeBuffer();
/**
* Allocate a {@link CompositeByteBuf} with the given maximum number of components that can be stored in it.
* If it is a direct or heap buffer depends on the actual implementation.
*/
CompositeByteBuf compositeBuffer(int maxNumComponents);
/**
* Allocate a heap {@link CompositeByteBuf}.
*/
CompositeByteBuf compositeHeapBuffer();
/**
* Allocate a heap {@link CompositeByteBuf} with the given maximum number of components that can be stored in it.
*/
CompositeByteBuf compositeHeapBuffer(int maxNumComponents);
/**
* Allocate a direct {@link CompositeByteBuf}.
*/
CompositeByteBuf compositeDirectBuffer();
/**
* Allocate a direct {@link CompositeByteBuf} with the given maximum number of components that can be stored in it.
*/
CompositeByteBuf compositeDirectBuffer(int maxNumComponents);
/**
* Returns {@code true} if direct {@link ByteBuf}'s are pooled
*/
boolean isDirectBufferPooled();
/**
* Calculate the new capacity of a {@link ByteBuf} that is used when a {@link ByteBuf} needs to expand by the
* {@code minNewCapacity} with {@code maxCapacity} as upper-bound.
*/
int calculateNewCapacity(int minNewCapacity, int maxCapacity);
}
其中 ByteBufAllocator 的具体实现可以查看其子类,如下图
下面来看看各自子类的功能以及区别
UnpooledByteBufAllocator
heap 内存分配
入口 new InstrumentedUnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
:
发现分配 Unpooled、Unsafe、Heap
内存,其实是分配了一个 byte 数组,并保存在 UnpooledHeapByteBuf#array
成员变量中。该内存的初始值容量和最大可扩展容量可以指定。
public UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {super(maxCapacity);
checkNotNull(alloc, "alloc");
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format("initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
}
this.alloc = alloc;
setArray(allocateArray(initialCapacity));
setIndex(0, 0);
}
protected byte[] allocateArray(int initialCapacity) {return new byte[initialCapacity];
}
查看 UnpooledHeapByteBuf#getByte()
方法,堆内存类型的 ByteBuf 获取的时候。直接通过下标获取 byte 数组中的 byte
@Override
public byte getByte(int index) {ensureAccessible();
return _getByte(index);
}
@Override
protected byte _getByte(int index) {// 该 array 为初始化的时候,实例化的 byte[]
return HeapByteBufUtil.getByte(array, index);
}
static byte getByte(byte[] memory, int index) {
// 直接拿到一个数组
return memory[index];
}
direct 内存分配
入口UnpooledByteBufAllocator#newDirectBuffer() --> UnpooledUnsafeDirectByteBuf
public UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {super(maxCapacity);
if (alloc == null) {throw new NullPointerException("alloc");
}
checkPositiveOrZero(initialCapacity, "initialCapacity");
checkPositiveOrZero(maxCapacity, "maxCapacity");
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format("initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
}
this.alloc = alloc;
setByteBuffer(allocateDirect(initialCapacity), false);
}
protected ByteBuffer allocateDirect(int initialCapacity) {return ByteBuffer.allocateDirect(initialCapacity);
}
final void setByteBuffer(ByteBuffer buffer, boolean tryFree) {if (tryFree) {
ByteBuffer oldBuffer = this.buffer;
if (oldBuffer != null) {if (doNotFree) {doNotFree = false;} else {freeDirect(oldBuffer);
}
}
}
this.buffer = buffer;
memoryAddress = PlatformDependent.directBufferAddress(buffer);
tmpNioBuf = null;
capacity = buffer.remaining();}
可以发现,Unpooled、Direct 类型得内存分配实际上是维护了一个底层 jdk 的一个 DirectByteBuffer。分配内存的时候就创建它,并将他保存到 buffer 成员变量。
跟踪iUnpooledHeapByteBuf#_getByte()
,就比较简单了,直接使用 jdk 的 api 获取
@Override
protected byte _getByte(int index) {
// 使用 buffer
return buffer.get(index);
}
更加详细的分析可以查看下面这篇文章
https://www.jianshu.com/p/158…
PooledByteBufAllocator
入口 PooledByteBufAllocator#newHeapBuffer()
和PooledByteBufAllocator#newDirectBuffer()
,堆内内存和堆外内存分配的模式都比较固定。
1. 拿到线程局部缓存 PoolThreadCache
2. 拿到不同类型的 rena
3. 使用不同类型的 arena 进行内存分配
@Override
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
// 拿到线程局部缓存
PoolThreadCache cache = threadCache.get();
// 拿到 heapArena
PoolArena<byte[]> heapArena = cache.heapArena;
final ByteBuf buf;
if (heapArena != null) {
// 使用 heapArena 分配内存
buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
} else {buf = PlatformDependent.hasUnsafe() ?
new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
return toLeakAwareBuffer(buf);
}
@Override
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
// 拿到线程局部缓存
PoolThreadCache cache = threadCache.get();
// 拿到 directArena
PoolArena<ByteBuffer> directArena = cache.directArena;
final ByteBuf buf;
if (directArena != null) {
// 使用 directArena 分配内存
buf = directArena.allocate(cache, initialCapacity, maxCapacity);
} else {buf = PlatformDependent.hasUnsafe() ?
UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
}
return toLeakAwareBuffer(buf);
}
跟踪 threadCache.get()
调用的是 FastThreadLocal#get()
方法。那么其实 threadCache 也是一个 FastThreadLocal, 可以看成是 jdk 的 ThreadLocal,get 方法调用了初始化方法initializel
public final V get() {InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {return (V) v;
}
// 调用初始化方法
V value = initialize(threadLocalMap);
registerCleaner(threadLocalMap);
return value;
}
initialValue()
方法的逻辑如下
1. 从预先准备好的
heapArenas
和directArenas
中获取最少使用的arena
2. 使用获取到的arean
为参数,实例化一个PoolThreadCache
并返回
final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
private final boolean useCacheForAllThreads;
PoolThreadLocalCache(boolean useCacheForAllThreads) {this.useCacheForAllThreads = useCacheForAllThreads;}
@Override
protected synchronized PoolThreadCache initialValue() {
/**
* arena 翻译成竞技场,关于内存非配的逻辑都在这个竞技场中进行分配
*/
// 获取 heapArena:从 heapArenas 堆内竞技场中拿出使用最少的一个 arena
final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
// 获取 directArena:从 directArena 堆内竞技场中拿出使用最少的一个 arena
final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
Thread current = Thread.currentThread();
if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
// 创建 PoolThreadCache:该 Cache 最终被一个线程使用
// 通过 heapArena 和 directArena 维护两大块内存:堆和堆外内存
// 通过 tinyCacheSize,smallCacheSize,normalCacheSize 维护 ByteBuf 缓存列表维护反复使用的内存块
return new PoolThreadCache(
heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
}
// No caching so just use 0 as sizes.
return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
}
// 省略代码......
}
查看 PoolThreadCache
其维护了两种类型的内存分配策略,一种是上述通过持有 heapArena
和directArena
,另一种是通过维护 tiny,small,normal
对应的缓存列表来维护反复使用的内存。
final class PoolThreadCache {private static final InternalLogger logger = InternalLoggerFactory.getInstance(PoolThreadCache.class);
// 通过 arena 的方式维护内存
final PoolArena<byte[]> heapArena;
final PoolArena<ByteBuffer> directArena;
// 维护了 tiny, small, normal 三种类型的缓存列表
// Hold the caches for the different size classes, which are tiny, small and normal.
private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
private final MemoryRegionCache<byte[]>[] normalHeapCaches;
private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;
// Used for bitshifting when calculate the index of normal caches later
private final int numShiftsNormalDirect;
private final int numShiftsNormalHeap;
private final int freeSweepAllocationThreshold;
private final AtomicBoolean freed = new AtomicBoolean();
private int allocations;
// TODO: Test if adding padding helps under contention
//private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena,
int tinyCacheSize, int smallCacheSize, int normalCacheSize,
int maxCachedBufferCapacity, int freeSweepAllocationThreshold) {checkPositiveOrZero(maxCachedBufferCapacity, "maxCachedBufferCapacity");
this.freeSweepAllocationThreshold = freeSweepAllocationThreshold;
// 通过持有 heapArena 和 directArena,arena 的方式管理内存分配
this.heapArena = heapArena;
this.directArena = directArena;
// 通过 tinyCacheSize,smallCacheSize,normalCacheSize 创建不同类型的缓存列表并保存到成员变量
if (directArena != null) {
tinySubPageDirectCaches = createSubPageCaches(tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
smallSubPageDirectCaches = createSubPageCaches(smallCacheSize, directArena.numSmallSubpagePools, SizeClass.Small);
numShiftsNormalDirect = log2(directArena.pageSize);
normalDirectCaches = createNormalCaches(normalCacheSize, maxCachedBufferCapacity, directArena);
directArena.numThreadCaches.getAndIncrement();} else {
// No directArea is configured so just null out all caches
tinySubPageDirectCaches = null;
smallSubPageDirectCaches = null;
normalDirectCaches = null;
numShiftsNormalDirect = -1;
}
if (heapArena != null) {
// Create the caches for the heap allocations
// 创建规格化缓存队列
tinySubPageHeapCaches = createSubPageCaches(tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
// 创建规格化缓存队列
smallSubPageHeapCaches = createSubPageCaches(smallCacheSize, heapArena.numSmallSubpagePools, SizeClass.Small);
numShiftsNormalHeap = log2(heapArena.pageSize);
// 创建规格化缓存队列
normalHeapCaches = createNormalCaches(normalCacheSize, maxCachedBufferCapacity, heapArena);
heapArena.numThreadCaches.getAndIncrement();} else {
// No heapArea is configured so just null out all caches
tinySubPageHeapCaches = null;
smallSubPageHeapCaches = null;
normalHeapCaches = null;
numShiftsNormalHeap = -1;
}
// Only check if there are caches in use.
if ((tinySubPageDirectCaches != null || smallSubPageDirectCaches != null || normalDirectCaches != null
|| tinySubPageHeapCaches != null || smallSubPageHeapCaches != null || normalHeapCaches != null)
&& freeSweepAllocationThreshold < 1) {
throw new IllegalArgumentException("freeSweepAllocationThreshold:"
+ freeSweepAllocationThreshold + "(expected: > 0)");
}
}
private static <T> MemoryRegionCache<T>[] createSubPageCaches(int cacheSize, int numCaches, SizeClass sizeClass) {if (cacheSize > 0 && numCaches > 0) {
//MemoryRegionCache 维护缓存的一个对象
@SuppressWarnings("unchecked")
MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
for (int i = 0; i < cache.length; i++) {
// TODO: maybe use cacheSize / cache.length
// 每一种 MemoryRegionCache(tiny,small,normal)都表示不同内存大小(不同规格)的一个队列
cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);
}
return cache;
} else {return null;}
}
private static <T> MemoryRegionCache<T>[] createNormalCaches(int cacheSize, int maxCachedBufferCapacity, PoolArena<T> area) {if (cacheSize > 0 && maxCachedBufferCapacity > 0) {int max = Math.min(area.chunkSize, maxCachedBufferCapacity);
int arraySize = Math.max(1, log2(max / area.pageSize) + 1);
//MemoryRegionCache 维护缓存的一个对象
@SuppressWarnings("unchecked")
MemoryRegionCache<T>[] cache = new MemoryRegionCache[arraySize];
for (int i = 0; i < cache.length; i++) {// 每一种 MemoryRegionCache(tiny,small,normal)都表示不同内存(不同规格)大小的一个队列
cache[i] = new NormalMemoryRegionCache<T>(cacheSize);
}
return cache;
} else {return null;}
}
......
}
更加详细分析可参考以下文章
https://www.jianshu.com/p/1cd…
directArena 分配 direct 内存的流程
上一步拿到 PoolThreadCache 之后,获取对应的Arena
。那么之后就是 Arena 具体分配内存的步骤。
入口 PooledByteBufAllocator#newDirectBuffer()
方法种有如下代码
PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
// 拿到 PooledByteBuf 对象, 仅仅是一个对象
PooledByteBuf<T> buf = newByteBuf(maxCapacity);
// 从 cache 种分配内存,并初始化 buf 种内存地址相关的属性
allocate(cache, buf, reqCapacity);
return buf;
}
可以看到分配的过程如下:拿到 PooledByteBuf 对象从 cache 中分配内存,并重置相关属性.
1.newByteBuf(maxCapacity);
拿到 PooledByteBuf 对象
@Override
protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {if (HAS_UNSAFE) {
// 获取一个 PooledByteBuf
return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
} else {return PooledDirectByteBuf.newInstance(maxCapacity);
}
}
static PooledUnsafeDirectByteBuf newInstance(int maxCapacity) {
// 从带有回收特性的对象池 RECYCLER 获取一个 PooledUnsafeDirectByteBuf
PooledUnsafeDirectByteBuf buf = RECYCLER.get();
//buf 可能是从回收站拿出来的,要进行复用
buf.reuse(maxCapacity);
return buf;
}
2.Recycler 是一个基于线程本地堆栈的对象池。Recycler 维护了一个 ThreadLocal 成员变量,用于返回一个 stack 给回收处理 DefaultHandle
,该处理器通过维护这个堆栈来维护PooledUnsafeDirectByteBuf
缓存。
private static final Recycler<PooledUnsafeDirectByteBuf> RECYCLER = new Recycler<PooledUnsafeDirectByteBuf>() {
@Override
protected PooledUnsafeDirectByteBuf newObject(Handle<PooledUnsafeDirectByteBuf> handle) {
//Recycler 负责用回收处理器 handler 维护 PooledUnsafeDirectByteBuf
//handler 底层持有一个 stack 作为对象池,维护对象池,handle 同时负责对象回收
// 存储 handler 为成员变量,使用完该 ByteBuf 可以调用回收方法回收
return new PooledUnsafeDirectByteBuf(handle, 0);
}
};
// 维护了一个 `ThreadLocal`,`initialValue` 方法返回一个堆栈。private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {
@Override
protected Stack<T> initialValue() {return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,
ratioMask, maxDelayedQueuesPerThread);
}
@Override
protected void onRemoval(Stack<T> value) {
// Let us remove the WeakOrderQueue from the WeakHashMap directly if its safe to remove some overhead
if (value.threadRef.get() == Thread.currentThread()) {if (DELAYED_RECYCLED.isSet()) {DELAYED_RECYCLED.get().remove(value);
}
}
}
};
3. 再看 Recycler#get()方法
public final T get() {if (maxCapacityPerThread == 0) {return newObject((Handle<T>) NOOP_HANDLE);
}
// 获取对应的堆栈,相当一个回收站
Stack<T> stack = threadLocal.get();
// 从栈顶拿出一个来 DefaultHandle(回收处理器)//DefaultHandle 持有一个 value,其实是 PooledUnsafeDirectByteBuf
DefaultHandle<T> handle = stack.pop();
// 没有回收处理器,说明没有闲置的 ByteBuf
if (handle == null) {
// 新增一个处理器
handle = stack.newHandle();
// 回调,还记得么?该回调返回一个 PooledUnsafeDirectByteBuf
// 让处理器持有一个新的 PooledUnsafeDirectByteBuf
handle.value = newObject(handle);
}
// 如果有,则可直接重复使用
return (T) handle.value;
}
public final V get() {InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {return (V) v;
}
// 回调 initialize
V value = initialize(threadLocalMap);
registerCleaner(threadLocalMap);
return value;
}
private V initialize(InternalThreadLocalMap threadLocalMap) {
V v = null;
try {
// 回调
v = initialValue();} catch (Exception e) {PlatformDependent.throwException(e);
}
threadLocalMap.setIndexedVariable(index, v);
addToVariablesToRemove(threadLocalMap, this);
return v;
}
DefaultHandle<T> newHandle() {
// 实例化一个处理器并并且初四话成员变量,该成员变量 stack 从 threalocal 中初始化
return new DefaultHandle<T>(this);
}
DefaultHandle 用 stack
作为缓存池维护 PooledUnsafeDirectByteBuf
, 同理PooledDirectByteBuf
也是一样的。只不过实例化的对象的实现不一样而已。同时,处理器定义了回收的方法是将兑现存回栈内,使用的时候则是从栈顶取出。
static final class DefaultHandle<T> implements Handle<T> {
private int lastRecycledId;
private int recycleId;
boolean hasBeenRecycled;
// 对象缓存池
private Stack<?> stack;
private Object value;
DefaultHandle(Stack<?> stack) {this.stack = stack;}
/**
* 定义回收方法,回收对象到 stack
* @param object
*/
@Override
public void recycle(Object object) {if (object != value) {throw new IllegalArgumentException("object does not belong to handle");
}
Stack<?> stack = this.stack;
if (lastRecycledId != recycleId || stack == null) {throw new IllegalStateException("recycled already");
}
// 回收:将自己存进栈中缓存起来
stack.push(this);
}
}
到这我们刚刚看完第一步,到第二步重置缓存内指针的时候了 , 获取到 PooledUnsafeDirectByteBuf 的时候,有可能是从缓存中取出来的。因此需要复用.
static PooledUnsafeDirectByteBuf newInstance(int maxCapacity) {
// 从带有回收特性的对象池 RECYCLER 获取一个 PooledUnsafeDirectByteBuf
PooledUnsafeDirectByteBuf buf = RECYCLER.get();
//buf 可能是从回收站拿出来的,要进行复用
buf.reuse(maxCapacity);
return buf;
}
final void reuse(int maxCapacity) {
// 重置最大容量
maxCapacity(maxCapacity);
// 设置引用
setRefCnt(1);
// 重置指针
setIndex0(0, 0);
// 重置标记值
discardMarks();}
到这才刚刚完成分配内存的第一步(拿到 PooledByteBuf 对象),以上都是仅仅是获取并且用回收站和回收处理器管理这些对象,这些对象仍然只是一个对象,还没有分配实际的内存。
跟踪PoolArena#allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity)
private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {final int normCapacity = normalizeCapacity(reqCapacity);
// 不同的规格大小进行内存分配
/**
* 分配整体逻辑(先判断 tiny 和 small 规格的,再判断 normal 规格的)* 1. 尝试从缓存上进行内存分配,成功则返回
* 2. 失败则再从内存堆中进行分配内存
*/
if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
int tableIdx;
PoolSubpage<T>[] table;
boolean tiny = isTiny(normCapacity);
// 尝试 tiny 和 small 规格的缓存内存分配
if (tiny) { // < 512
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = tinyIdx(normCapacity);
table = tinySubpagePools;
} else {if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = smallIdx(normCapacity);
table = smallSubpagePools;
}
final PoolSubpage<T> head = table[tableIdx];
/**
* Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
* {@link PoolChunk#free(long)} may modify the doubly linked list as well.
*/
synchronized (head) {
final PoolSubpage<T> s = head.next;
if (s != head) {
assert s.doNotDestroy && s.elemSize == normCapacity;
long handle = s.allocate();
assert handle >= 0;
s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
incTinySmallAllocation(tiny);
return;
}
}
//tiny 和 small 规格的缓存内存分配尝试失败
// 从内存堆中分配内存
synchronized (this) {allocateNormal(buf, reqCapacity, normCapacity);
}
incTinySmallAllocation(tiny);
return;
}
//normal 规格
// 如果分配处出来的内存大于一个值(chunkSize),则执行 allocateHuge
if (normCapacity <= chunkSize) {
// 从缓存上进行内存分配
if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
// 缓存没有再从内存堆中分配内存
synchronized (this) {allocateNormal(buf, reqCapacity, normCapacity);
++allocationsNormal;
}
} else {
// Huge allocations are never served via the cache so just call allocateHuge
allocateHuge(buf, reqCapacity);
}
}
其整体分配内存的逻辑是根据不同规格大小的内存需要来的,显示 tiny
和small
规格的,再是 normal
规格的。分配也是先尝试从缓存中进行内存分配,如果分配失败再从内存堆中进行内存分配。当然,分配出来的内存回和第一步拿到的 PooledByteBuf 进行绑定起来。
总结
主要学习了 ByteBuf 的 基本结构、使用模式、分类、基本的内存分配。
下次再学习 ByteBuf 的 命中逻辑以及内存回收。
参考文章
https://www.cnblogs.com/xiang…
https://www.jianshu.com/u/fc9…
最后
如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。
关注公众号 【爱编码】,回复2019 有相关资料哦。