Netty学习之旅----源码分析内存分配与释放原理

long bits = bitmap[i];

if (~bits != 0) { //@3

return findNextAvail0(i, bits); //@4

}

}

return -1;

}

代码@2:遍历bitmap,根据当前的elmSize可以得知当前用来多少个long来代表总的长度。

代码@3:如果取反不为0,则说明用这个long代表的64个PoolSubpage未使用完,可用从这里获取一个,故进入到代码@4。

private int findNextAvail0(int i, long bits) {

final int maxNumElems = this.maxNumElems;

final int baseVal = i << 6; //@5

for (int j = 0; j < 64; j ++) { //遍历,一个long总代表64个PoolSubpage

if ((bits & 1) == 0) { //@6

int val = baseVal | j; //@8

if (val < maxNumElems) {

return val;

} else {

break;

}

}

bits >>>= 1; //@7

}

return -1;

}

代码@5:baseVal = i << 6,也就是i * 64,如果i在bitmap数组中的下标为0,baseVal则等于0,如果下标为1,则baseVal=64,如果下标为2,则baseVal=128。

代码@6:用bits&1判断是否等于0,也就是判断bits的低位是否被占用,如果为1,则表示被占用,如果为0,表示未被占用,如果结果为0,则可以返回该值了,如果不为0,则无符号向右移动1位,相当与去掉最后一位,继续比较。从这里就可以看出,一个long从低位开始被标记。我们举例说明一下,比如现在 用两个long类型可以标记所有的PoolSubpage,比如bitmap[0] = (00000000 00000000 00000000 00000000 00000000 00000000 00000000 0111111),表示已经分配了7个PoolSubpage。

代码 toHandle方法详解:

private long toHandle(int bitmapIdx) {

return 0xb000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;

}

该值,主要是用一个long类型的值的低32位来表示 memoryMapIdx,用高32位表示 bitmapIdx,这里为什么需要与0xb000000000000000L进行或运算呢?据我的目前掌握的知识看,主要是将0映射为一个数字,主要来区分是在PoolSubpage中分配的,还是内存大于pageSize,在内存释放的时候,可以通过bitmaIdx >> 32 & 0x3FFFFFFF 得出原来的索引。

1.3.1.4 关于 PoolArena allocate代码@4 initBuf详解:

void initBuf(PooledByteBuf buf, long handle, int reqCapacity) {

int memoryMapIdx = (int) handle;//@1

int bitmapIdx = (int) (handle >>> Integer.SIZE); //@2

if (bitmapIdx == 0) {//@3

byte val = value(memoryMapIdx);

assert val == unusable : String.valueOf(val);

buf.init(this, handle, runOffset(memoryMapIdx), reqCapacity, runLength(memoryMapIdx));

} else {//@4

initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);

}

}

首先这里对入参 long handle做一个详解:如果需要分配的内存大于pageSize,则返回的高32为为0,低32位为memoryMap的下标id。具体参照下文的allocateRun方法讲解;如果需要分配的内存小于pageSize,则返回的高32为为bitmapIdx,低32位同样表示memoryMap的id。

代码@1:从long中获取memoryMapIdx。

代码@2:从long中获取bitmapIdx。

代码@3:如果bitmapIdx为0,偏移量就是PoolSubpage的偏移量。为什么呢?bitmapIdx为0在内存申请大于pageSize和小于pageSize时都会出现:

  • 如果申请的内存数小于pageSize,bitmapIdx表示该pageSubpage是第一次分配。

  • 如果申请的内存数大于pageSize,表明该节点所以子节点都是第一次被分片。故偏移量就是memory[id]所代表的偏移量。

代码@4:如果bitmapIdx不为0,需要计算偏移量,与PoolSubpage的elemSize相关,具体请看2)PoolChunk的initBufWithSubpage方法,该方法,最终还是要调用PooledByteBuf的init方法,分配内存,这里只是需要计算偏远量。

1、PooledByteBuf的 int方法

void init(PoolChunk chunk, long handle, int offset, int length, int maxLength) {

assert handle >= 0;

assert chunk != null;

this.chunk = chunk;

this.handle = handle;

memory = chunk.memory; // @1

this.offset = offset; // @2

this.length = length; // @3

this.maxLength = maxLength; // @4

setIndex(0, 0); //@5

tmpNioBuf = null;

initThread = Thread.currentThread();//@6

}

代码@1:PooledByteBuf的内存,直接指向PoolChunk的memory;

代码@2:offset,在memory的起始偏移量。

代码@3:memory的offset + length之间的内存被该PooledByteBuf占用,其他ByteBuf无法使用。

代码@4:maxLength的作用是什么呢?目前我的理解是,PooledByteBuf自动扩容时,只要最终长度不超过maxLength,就可以在当前的内存中完成,不需要去申请新的空间,再进行内存负责。

代码@5:初始化readIndex,writeIndex。

代码@6:记录该PooledByteBuf的初始化线程,方便本地线程池的使用。

2、PoolChunk的iinitBufWithSubpage方法详解

private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx, int reqCapacity) {

assert bitmapIdx != 0;

int memoryMapIdx = (int) handle;

PoolSubpage subpage = subpages[subpageIdx(memoryMapIdx)];

assert subpage.doNotDestroy;

assert reqCapacity <= subpage.elemSize;

buf.init(

this, handle,

runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize, reqCapacity, subpage.elemSize);

}

1.3.2 PoolChunk allocate关于代码@allocateRun,超过pageSize内存分配源码分析

/**

  • Allocate a run of pages (>=1)

  • @param normCapacity normalized capacity

  • @return index in memoryMap

*/

private long allocateRun(int normCapacity) {

int d = maxOrder - (log2(normCapacity) - pageShifts); //@1

int id = allocateNode(d); //@2

if (id < 0) {

return id;

}

freeBytes -= runLength(id); //@3

return id;

}

该方法的实现,就是要在平衡二叉树中要找到一个可以满足normCapacity的节点,从前文的介绍中得知,memoryMap[id]存放的是,该id能分配的最小深度代表的容量。超过pageSize的内存节点,肯定不是在叶子节点。如果让我实现查找合适id的算法,我想应该是这样的:

1、算成需要分配的内存大小是 pageSize的倍数,再直观点就是normalCapacity 是 pageSize 的倍数n,然后算成n是2的多少次幂,比如pageSize=4,而normalCapacity是16,得出的倍数是4,4是2的2次幂,最终要得到这个值。标记为r

2、然后从平衡二叉树,沿着最底层(高度为0,深度为maxOrder),向上找r级,即能知道这一层次的节点可以容纳下normaCapacity的节点。

代码@1:先算出normCapacity,2的对(2的幂)然后减去pageSize(2的幂),得出思路1的r的值,然后用maxOrder-r,就表示深度最大为maxOrder-r。

代码@2:沿着平衡二叉树,从根节点开始,寻找一个合适的节点。如果无法分配,返回-1。

allocateNode(id)在上文中已经讲解,为了增深映像,在这里将代码再次粘贴:

private int allocateNode(int d) {

int id = 1;

int initial = - (1 << d); // has last d bits = 0 and rest all = 1

byte val = value(id);

if (val > d) { // unusable

return -1;

}

while (val < d || (id & initial) == 0) { // id & initial == 1 << d for all ids at depth d, for < d it is 0

id <<= 1;

val = value(id);

if (val > d) {

id ^= 1;

val = value(id);

}

}

byte value = value(id);

assert value == d && (id & initial) == 1 << d : String.format(“val = %d, id & initial = %d, d = %d”,

value, id & initial, d);

setValue(id, unusable); // mark as unusable

updateParentsAlloc(id);

return id;

}

1.3.3 关于PoolArena 2.2.2 allocate方法的代码@5allocateHuge方法详解

在netty内存管理中,如果超过chunkSize的内存,为大内存,不重复使用。

private void allocateHuge(PooledByteBuf buf, int reqCapacity) {

buf.initUnpooled(newUnpooledChunk(reqCapacity), reqCapacity);

}

PoolArena.HeapArena:

protected PoolChunk<byte[]> newUnpooledChunk(int capacity) {

return new PoolChunk<byte[]>(this, new byte[capacity], capacity);

}

PoolChunk非池管理的PoolChunk:

/** Creates a special chunk that is not pooled. */

PoolChunk(PoolArena arena, T memory, int size) {

unpooled = true;

this.arena = arena;

this.memory = memory;

memoryMap = null;

depthMap = null;

subpages = null;

subpageOverflowMask = 0;

pageSize = 0;

pageShifts = 0;

maxOrder = 0;

unusable = (byte) (maxOrder + 1);

chunkSize = size;

log2ChunkSize = log2(chunkSize);

maxSubpageAllocs = 0;

}

大内存的PoolChunk,不缓存使用,故内部不会再细化为PoolSubpage等数据结构。

void initUnpooled(PoolChunk chunk, int length) {

assert chunk != null;

this.chunk = chunk;

handle = 0;

memory = chunk.memory;

offset = 0;

this.length = maxLength = length;

setIndex(0, 0);

tmpNioBuf = null;

initThread = Thread.currentThread();

}

PooledByteBuf来初始化是,lengt,maxLenth等于需要申请的内存。看过tiny,small内存的分配后,大内存的分配比较简单,就不做每行代码的解读。不容易呀,Netty内存的分配就讲解完毕了。接下来注重分析两个方面:内存释放与PooledByteBuf动态扩容。

2、源码分析Netty内存释放

===============

在讲解Netty内存释放之前,我们还是简单的再回顾一下ReferenceCounted,Netty的ByteBuf继承该接口,这也表明,Netty内部的内存管理是基于引用计数来进行内存的回收的。具体的实现是AbstractReferenceCounted。所以,我们在使用PooledByteBuf时,用完后要记得调用release方法。在线程本地分配时会再次强调,如果PooledByteBuf在用完后,没有调用realse方法,是无法被线程共用的。内存的释放入口,是ByteBuf的relase方法,也就是AbstractReferenceCountedByteBuf的release方法:

public final boolean release(int decrement) {

if (decrement <= 0) {

throw new IllegalArgumentException(“decrement: " + decrement + " (expected: > 0)”);

}

for (;😉 {

int refCnt = this.refCnt;

if (refCnt < decrement) {

throw new IllegalReferenceCountException(refCnt, -decrement);

}

if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {

if (refCnt == decrement) {

deallocate();

return true;

}

return false;

}

}

}

该方法的方式是循环利用CAS将引用减少,直到引用为0,则调用释放方法,ByteBuf的内存释放,不同子类,不同的实现,故deallocate方法为抽象方法;

/**

  • Called once {@link #refCnt()} is equals 0.

*/

protected abstract void deallocate();

3.1 PooledByteBuf deallocate方法详解

protected final void deallocate() {

if (handle >= 0) {

final long handle = this.handle;

this.handle = -1;

memory = null;

boolean sameThread = initThread == Thread.currentThread();

initThread = null;

chunk.arena.free(chunk, handle, maxLength, sameThread); //@1

recycle(); //@2

}

}

内存释放的基本实现:首先将PooledByteBuf内部的handle修改为-1,将memory设置为null,,然后释放占用的内存,然后将该PooledByteBuf放入对象池。从这里也可以看出,PooledByteBuf ,不仅ByteBuf内部持有的缓存区会被回收,连PooledByteBuf这个对象本身,也会放入到对象池,供重复利用(线程级)。

2.1关于PooledByteBuf deallocate 代码@1 PoolArena的free方法,内存释放


/**

  • @param chunk

  • @param handle,内存分配,保存着bitmapIdx,memoryMapIdx

  • @param normaCapacity 申请的内存(释放的内存,取值为PooledByteBuf的 maxLenth)

  • @param 释放该PooledByteBuf的线程释放时创建该PooledByteBuf的线程

*/

void free(PoolChunk chunk, long handle, int normCapacity, boolean sameThreads) {

if (chunk.unpooled) { //@1

destroyChunk(chunk);

} else {

if (sameThreads) { //@2

PoolThreadCache cache = parent.threadCache.get();

if (cache.add(this, chunk, handle, normCapacity)) {

// cached so not free it.

return;

}

}

synchronized (this) { //@3

chunk.parent.free(chunk, handle);

}

}

}

如果是非池的内存,也就是大内存,直接释放,如果释放的线程是创建该PooledByteBuf的线程,则放入线程本地变量中,重复利用,此时不需要释放。如果不是,则释放内存。

代码@1:我们看一下PoolArena.HeapArena和DirectArena的分别实现:

PoolArena.HeapArena

protected void destroyChunk(PoolChunk<byte[]> chunk) {

// Rely on GC.

}

PoolArena.DirectArena:

protected void destroyChunk(PoolChunk chunk) {

PlatformDependent.freeDirectBuffer(chunk.memory); //释放堆外内存

}

代码@2:放入本地线程缓存中,这个稍后重点关注。

代码@3:释放内存,这里的内存释放与第一步的内存释放不同,第一步的内存释放,是直接将内存还给JVM堆、或操作系统。而这里的是在PoolChunk中进行标记与释放而已。chunk.parent指的是该PoolChunk的PoolChunkList对象。先重点进入到PoolChunk的free方法。

2.2 关于PoolArena.free方法 代码@3的讲解,此处主要是调用PoolChunk方法进行内存的释放:


/**

  • Free a subpage or a run of pages

  • When a subpage is freed from PoolSubpage, it might be added back to subpage pool of the owning PoolArena

  • If the subpage pool in PoolArena has at least one other PoolSubpage of given elemSize, we can

  • completely free the owning Page so it is available for subsequent allocations

  • @param handle handle to free

*/

void free(long handle) {

int memoryMapIdx = (int) handle; //@1

int bitmapIdx = (int) (handle >>> Integer.SIZE); //@2

if (bitmapIdx != 0) { // free a subpage //@3

PoolSubpage subpage = subpages[subpageIdx(memoryMapIdx)];

assert subpage != null && subpage.doNotDestroy;

if (subpage.free(bitmapIdx & 0x3FFFFFFF)) {

return;

}

}

freeBytes += runLength(memoryMapIdx); //@4

setValue(memoryMapIdx, depth(memoryMapIdx)); //@5

updateParentsFree(memoryMapIdx); //@6

}

代码@1:获取在memoryMap的下标。

代码@2:获取bitmapIdx。

代码@3:如果bitmapIdx不等于0,表示该内存是从PoolSubpage中直接分配的(内存申请时,小于pageSize)。此时内存的释放,直接调用PoolSubpage的free方法。

代码@4,@5,@6:如果申请的内存超过了pageSize,首先将freeBytes增加,然后在代码@5,将memoryMapIdx处的值更新为原先depth的值,代表可以重新分片深度为depth[id]的内存了,级联更新memoryMap,的父节点的释放状态。

2.3 关于 PoolArena.free方法,代码@3, PoolSubpage.free方法详解。


/**

  • @return {@code true} if this subpage is in use.

  •     {@code false} if this subpage is not used by its chunk and thus it's OK to be released.
    

*/

boolean free(int bitmapIdx) {

if (elemSize == 0) {

return true;

}

int q = bitmapIdx >>> 6; //@1

int r = bitmapIdx & 63; //@2

assert (bitmap[q] >>> r & 1) != 0;

bitmap[q] ^= 1L << r; //@3

setNextAvail(bitmapIdx); //@4

if (numAvail ++ == 0) { //@5 start

addToPool();

return true;

}

if (numAvail != maxNumElems) {

return true;

} else {

// Subpage not in use (numAvail == maxNumElems)

if (prev == next) {

// Do not remove if this subpage is the only one left in the pool.

return true;

}

// Remove this subpage from the pool if there are other subpages left in the pool.

doNotDestroy = false;

removeFromPool();

return false;

} //@5 end

}

代码@1:求出bitmapIdx在bitmap数组中的下标。

代码@3:就是bitmapIdx在bitmap[q]下所对应的高位1,设置为0,表示可以再次被用来分配。

代码@4:将bitmapIdx设置为下一个可以用nextAvail,这时在nextAvail>0,分配时候就会直接先用释放的,保证内存的连续性。

代码@5:如果PoolSubpage有剩余空间后,加入到PoolArena的tinyPoolSubpages[]或smallPoolSubpages[]中。如果空间全部分配后,或一个都没分配,从PoolArena的tinyPoolSubpages[],smallPoolSubpages[]中移除。

3.3 关于PoolArena.free方法,代码@6,级联更新释放状态详解


/**

  • Update method used by free

  • This needs to handle the special case when both children are completely free

  • in which case parent be directly allocated on request of size = child-size * 2

  • @param id id

*/

private void updateParentsFree(int id) {

int logChild = depth(id) + 1;

while (id > 1) {

int parentId = id >>> 1;

byte val1 = value(id);

byte val2 = value(id ^ 1);

logChild -= 1; // in first iteration equals log, subsequently reduce 1 from logChild as we traverse up

if (val1 == logChild && val2 == logChild) {

setValue(parentId, (byte) (logChild - 1));

} else {

byte val = val1 < val2 ? val1 : val2;

setValue(parentId, val);

}

id = parentId;

}

}

这个算法在理解了上文的updateParentsAllocator()方法后,不难理解,只要我们明确一点,memoryMap[id]=order,表示id代表的节点,可以胜任大于等于order深度的内存分配需要。

3、PooledByteBuf内存扩容

===================

在讲解这个问题的时候,不知道大家有没有注意到PooledByteBuf的maxLength属性?该属性有和作用,请看下文分解。

PooledByteBuf扩容算法,请看capacity(int newCapacity);

@Override

public final ByteBuf capacity(int newCapacity) {

ensureAccessible();

// If the request capacity does not require reallocation, just update the length of the memory.

if (chunk.unpooled) { //@1

if (newCapacity == length) {

return this;

}

} else {

if (newCapacity > length) { //@2

if (newCapacity <= maxLength) {

length = newCapacity;

return this;

}

} else if (newCapacity < length) { //@3

if (newCapacity > maxLength >>> 1) {

if (maxLength <= 512) {

if (newCapacity > maxLength - 16) {

length = newCapacity;

setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));

return this;

}

} else { // > 512 (i.e. >= 1024)

length = newCapacity;

setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));

return this;

}

}

} else {

return this;

}

}

// Reallocation required.

chunk.arena.reallocate(this, newCapacity, true); //@4

return this;

}

代码@1:如果是非池化的,并且新的容量等于length的话,可以直接返回,否则需要重新去申请内存。

代码@2:如果需要的内存大于length,此时newCapacity小于等于maxLength,则不需要扩容,如果需要的大小超过maxLength,则需要重新去申请内存。那这里的maxLength是什么呢?其实,如果PooledByteBuf的内存是在PoolSubpage中分配,那maxLength为PooledSubpage的内存区域中的总容量,PoolSubpage的MemoryRegion的总大小。也就是在是同一个时刻,一个PoolSubpage的MemoryRegion只能被一个PooledByteBuf所占用。

代码@3:如果需要申请的内存小于length,为了避免下一次需要扩容,再进一步判断,如果是tiny内存的话,判断newCapaciy与 maxLength-16的关系,如果不大于的话,本次也进行重新分配内存,然后维护PooledByteBuf的length,readerIndex,writerIndex。

代码@4:内存的重新分配 reallocate方法;

void reallocate(PooledByteBuf buf, int newCapacity, boolean freeOldMemory) {

if (newCapacity < 0 || newCapacity > buf.maxCapacity()) {

throw new IllegalArgumentException("newCapacity: " + newCapacity);

}

int oldCapacity = buf.length;

if (oldCapacity == newCapacity) {

return;

}

PoolChunk oldChunk = buf.chunk;

long oldHandle = buf.handle;

T oldMemory = buf.memory;

int oldOffset = buf.offset;

int oldMaxLength = buf.maxLength;

int readerIndex = buf.readerIndex();

int writerIndex = buf.writerIndex();

allocate(parent.threadCache.get(), buf, newCapacity); //@1

if (newCapacity > oldCapacity) { //@2 start

memoryCopy(

oldMemory, oldOffset,

buf.memory, buf.offset, oldCapacity);

} else if (newCapacity < oldCapacity) {

if (readerIndex < newCapacity) {

if (writerIndex > newCapacity) {

writerIndex = newCapacity;

}

memoryCopy(

oldMemory, oldOffset + readerIndex,

buf.memory, buf.offset + readerIndex, writerIndex - readerIndex);

} else {

readerIndex = writerIndex = newCapacity;

}

} // @2end

buf.setIndex(readerIndex, writerIndex);

if (freeOldMemory) {

free(oldChunk, oldHandle, oldMaxLength, buf.initThread == Thread.currentThread()); //@3

}

}

内存重新分配的算法如下:

首先重新申请内存,然后进行内存复制,最后释放原先的内存。

PoolArena.HeapArena的memoryCopy实现代码:

如何快速更新自己的技术积累?

  • 在现有的项目里,深挖技术,比如用到netty可以把相关底层代码和要点都看起来。
  • 如果不知道目前的努力方向,就看自己的领导或公司里技术强的人在学什么。
  • 知道努力方向后不知道该怎么学,就到处去找相关资料然后练习。
  • 学习以后不知道有没有学成,则可以通过面试去检验。

我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!

以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目

八年CRUD,疫情备战三个月,三面头条、四面阿里拿offer面经分享

八年CRUD,疫情备战三个月,三面头条、四面阿里拿offer面经分享

emoryCopy(

oldMemory, oldOffset,

buf.memory, buf.offset, oldCapacity);

} else if (newCapacity < oldCapacity) {

if (readerIndex < newCapacity) {

if (writerIndex > newCapacity) {

writerIndex = newCapacity;

}

memoryCopy(

oldMemory, oldOffset + readerIndex,

buf.memory, buf.offset + readerIndex, writerIndex - readerIndex);

} else {

readerIndex = writerIndex = newCapacity;

}

} // @2end

buf.setIndex(readerIndex, writerIndex);

if (freeOldMemory) {

free(oldChunk, oldHandle, oldMaxLength, buf.initThread == Thread.currentThread()); //@3

}

}

内存重新分配的算法如下:

首先重新申请内存,然后进行内存复制,最后释放原先的内存。

PoolArena.HeapArena的memoryCopy实现代码:

如何快速更新自己的技术积累?

  • 在现有的项目里,深挖技术,比如用到netty可以把相关底层代码和要点都看起来。
  • 如果不知道目前的努力方向,就看自己的领导或公司里技术强的人在学什么。
  • 知道努力方向后不知道该怎么学,就到处去找相关资料然后练习。
  • 学习以后不知道有没有学成,则可以通过面试去检验。

我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!

以上面试专题的答小编案整理成面试文档了,文档里有答案详解,以及其他一些大厂面试题目

[外链图片转存中…(img-7E5QWipf-1714430790328)]

[外链图片转存中…(img-Cmp1DqfB-1714430790329)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FastThreadLocal 是 Netty 中的一个优化版 ThreadLocal 实现。与 JDK 自带的 ThreadLocal 相比,FastThreadLocal 在性能上有所提升。 FastThreadLocal 的性能优势主要体现在以下几个方面: 1. 线程安全性:FastThreadLocal 使用了一种高效的方式来保证线程安全,避免了使用锁的开销,使得在高并发场景下性能更好。 2. 内存占用:FastThreadLocal 的内部数据结构更加紧凑,占用的内存更少,减少了对堆内存的占用,提高了内存的利用效率。 3. 访问速度:FastThreadLocal 在访问时,使用了直接索引的方式,避免了哈希表查找的开销,使得访问速度更快。 在 Netty 源码中,FastThreadLocal 主要被用于优化线程的局部变量存储,提高线程之间的数据隔离性和访问效率。通过使用 FastThreadLocal,Netty 在高性能的网络通信中能够更好地管理线程的局部变量,提供更高的性能和并发能力。 引用中提到的代码片段展示了 Netty 中的 InternalThreadLocalMap 的获取方式。如果当前线程是 FastThreadLocalThread 类型的线程,那么就直接调用 fastGet 方法来获取 InternalThreadLocalMap 实例;否则,调用 slowGet 方法来获取。 fastGet 方法中,会先尝试获取线程的 threadLocalMap 属性,如果不存在则创建一个新的 InternalThreadLocalMap,并设置为线程的 threadLocalMap 属性。最后返回获取到的 threadLocalMap。 slowGet 方法中,通过调用 UnpaddedInternalThreadLocalMap.slowThreadLocalMap 的 get 方法来获取 InternalThreadLocalMap 实例。如果获取到的实例为 null,则创建一个新的 InternalThreadLocalMap,并将其设置到 slowThreadLocalMap 中。最后返回获取到的 InternalThreadLocalMap。 综上所述,FastThreadLocal 是 Netty 中为了优化线程局部变量存储而设计的一种高性能的 ThreadLocal 实现。它通过减少锁的开销、优化内存占用和加快访问速度来提升性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [FastThreadLocal源码分析](https://blog.csdn.net/lvlei19911108/article/details/118021402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Netty 高性能之道 FastThreadLocal 源码分析(快且安全)](https://blog.csdn.net/weixin_33871366/article/details/94653953)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值