Netty内存分配

太过爱你忘了你带给我的痛 2022-09-04 02:56 243阅读 0赞

文章目录

  • 基本概念
  • PooledByteBufAllocator
    • newDirectBuffer
    • PoolThreadCache
      • initCache
  • PoolArena
    • PoolChunkList
    • PoolChunk
      • memoryMap
      • depthMap
      • subpages
    • PoolSubpage
      • bitmap
    • 分配原理
      • ★遵循的原则
      • PoolThreadCache分配内存
      • PoolChunk里面分配内存
        • allocate
        • allocateRun
        • allocateNode
        • allocateSubpage
          • PoolSubpage#allocate
    • 回收原理
      • PoolThreadCache回收内存
      • 通过chunk来释放内存
        • PoolChunkList#free
        • PoolChunk#free
        • PoolSubpage#free
    • 扩充内存块
  • 对比PoolArena和PoolThreadCache

基本概念

Netty内存根据使用的内存位置(堆内heap和堆外direct)和内存是否池化进行分类。

对于每个线程而言,netty会为之分配一个内存Cache;而在多个线程之间可共享一个Arena。Arena管理着相关内存,包含不同使用率的PoolChunkList、tinySubPagePools及smallSubPagePools来更好地分配内存。

内存根据大小可分为 huge、normal、small、tiny。

  • huge:大于16M内存。
  • normal:在8k-16M之间的内存。
  • small:在512B到8K之间的内存。共有4种尺寸,分别是512B,1024B,2048B,4096B。
  • tiny:小于512B的内存。最小为16B,按照16B来递增大小,区间为【16,512),共有32中尺寸。

由于初次申请内存,都是按照Chunk来申请,但是为了更高效率的使用内存,在Chunk这个级别下,还定义了Page和SubPage的内存块。

Chunk:默认大小是16M。在分配大小超过8K的内存,会从PoolChunkList中分配内存,或新增Chunk。一个Chunk会被分成2048个Page,是一个完全二叉树。一般每层节点有一个标识,标识当前节点及以下节点是否还有可用节点。

Page:默认大小是8K。通常使用subpageOverflowMask进行与运算判断一个大小是否小于8K.

SubPage: 管理小于8K的内存块(element)。

PoolSubPage:用于管理SubPage。

PooledByteBufAllocator

内存分配者。

在初始化时,会记录以下重要信息,然后在构造函数中创建数个PoolArena,后续分配内存时,其实是依靠PoolArena和PoolThreadCache。

而每个内存块最终是以Bytebuffer的实体来使用的,所以ByteBuffer中会存有对应的chunk(arena);在释放内存时,会通过PoolArena和PoolThreadCache来实现内存释放。

newDirectBuffer

  1. protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
  2. //这里可能会初始化当前线程的PoolThreadCache,此时也会匹配最少使用的arena,初始化各内存块缓存
  3. PoolThreadCache cache = threadCache.get();
  4. PoolArena<ByteBuffer> directArena = cache.directArena;
  5. final ByteBuf buf;
  6. if (directArena != null) {
  7. //使用arena来分配,此内部就是结合了PoolThreadCache分配
  8. buf = directArena.allocate(cache, initialCapacity, maxCapacity);
  9. } else {
  10. buf = PlatformDependent.hasUnsafe() ?
  11. UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
  12. new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
  13. }
  14. return toLeakAwareBuffer(buf);
  15. }

PoolThreadCache

内部主要维护了三个内存数组 tinySubPageXXXCaches,smallSubPageXXXCaches 和 normalXXXCaches。每个数组的大小分别是 32,4和3。每个数组内部存储的类型都是MemoryRegionCache,这个类里面维护了queue,用于存储内存块。

initCache

  1. protected synchronized PoolThreadCache initialValue() {
  2. //找到使用最少的Arena(评判标准是PoolArena#numThreadCaches)
  3. final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
  4. final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
  5. Thread current = Thread.currentThread();
  6. if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
  7. return new PoolThreadCache(
  8. heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
  9. DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
  10. }
  11. // No caching so just use 0 as sizes.
  12. return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
  13. }
  14. PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena,
  15. int tinyCacheSize, int smallCacheSize, int normalCacheSize,
  16. int maxCachedBufferCapacity, int freeSweepAllocationThreshold) {
  17. if (maxCachedBufferCapacity < 0) {
  18. throw new IllegalArgumentException("maxCachedBufferCapacity: "
  19. + maxCachedBufferCapacity + " (expected: >= 0)");
  20. }
  21. this.freeSweepAllocationThreshold = freeSweepAllocationThreshold;
  22. this.heapArena = heapArena;
  23. this.directArena = directArena;
  24. //创建tiny,small,normal cache数组
  25. if (directArena != null) {
  26. tinySubPageDirectCaches = createSubPageCaches(
  27. tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
  28. smallSubPageDirectCaches = createSubPageCaches(
  29. smallCacheSize, directArena.numSmallSubpagePools, SizeClass.Small);
  30. numShiftsNormalDirect = log2(directArena.pageSize);
  31. normalDirectCaches = createNormalCaches(
  32. normalCacheSize, maxCachedBufferCapacity, directArena);
  33. directArena.numThreadCaches.getAndIncrement();
  34. } else {
  35. // No directArea is configured so just null out all caches
  36. tinySubPageDirectCaches = null;
  37. smallSubPageDirectCaches = null;
  38. normalDirectCaches = null;
  39. numShiftsNormalDirect = -1;
  40. }
  41. if (heapArena != null) {
  42. // Create the caches for the heap allocations
  43. tinySubPageHeapCaches = createSubPageCaches(
  44. tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
  45. smallSubPageHeapCaches = createSubPageCaches(
  46. smallCacheSize, heapArena.numSmallSubpagePools, SizeClass.Small);
  47. numShiftsNormalHeap = log2(heapArena.pageSize);
  48. normalHeapCaches = createNormalCaches(
  49. normalCacheSize, maxCachedBufferCapacity, heapArena);
  50. heapArena.numThreadCaches.getAndIncrement();
  51. } else {
  52. // No heapArea is configured so just null out all caches
  53. tinySubPageHeapCaches = null;
  54. smallSubPageHeapCaches = null;
  55. normalHeapCaches = null;
  56. numShiftsNormalHeap = -1;
  57. }
  58. // Only check if there are caches in use.
  59. if ((tinySubPageDirectCaches != null || smallSubPageDirectCaches != null || normalDirectCaches != null
  60. || tinySubPageHeapCaches != null || smallSubPageHeapCaches != null || normalHeapCaches != null)
  61. && freeSweepAllocationThreshold < 1) {
  62. throw new IllegalArgumentException("freeSweepAllocationThreshold: "
  63. + freeSweepAllocationThreshold + " (expected: > 0)");
  64. }
  65. }

PoolArena

我们可以理解arena是公用的一个内存缓存。其中包含了:

  • 不同使用率的Chunk :qInit、q000、q025、q050、q075、q100
  • Subpages: tinySubpagePools,smallSubpagePools,用于存储没有被使用的小内存块

PoolChunkList

在Arena中存储的是PoolChunkList,这是一个双向的节点。对于chunk,arena会按照不同的使用率来管理,那么不同使用率的chunkList如何连接起来,此时就出现了PoolChunkList。

每个PoolChunkList内部会记录以下信息:

  1. nextList 下一个PoolChunkList(使用率更高的)
  2. prevList 前一个PoolChunkList(使用率更低的)
  3. minUsage 最低使用率,低于该值,会移除该chunk,放到preList
  4. maxUsage 最高使用率,高于该值,会移除该chunk,放到nextList
  5. maxCapacity: 最大可分配的内存大小,就是用minUsage计算的

在Arena中存在qInit、q000、q025、q050、q075和q100 六个PoolChunkList。























































prevList nextList minUsage maxUsage
qInit qInit q000 Integer.MIN_VALUE 25
q000 null q025 1 50
q025 q000 q050 25 75
q050 q025 q075 50 100
q075 q050 q100 75 100
q100 q075 null 100 Integer.MAX_VALUE

对于新创建的chunk,会先加入到qInit中。

对于PoolChunkList的使用,最终还是会落到Chunk上。

PoolChunk

memoryMap

我们都知道chunk是一个二叉树,那么需要一个专门存储每一个节点的分配信息,这就是memoryMap。按照默认的规则,chunk是16M,而每个page是8k,所以会有4095个节点。把4095个节点全部记录下来,例如:{0,1,1,2,2,2,2…},用于表示每个节点下面有多少未分配的内存块。

根据memoryMap记录的值,如何看出未分配的内存呢?

首先,二叉树中每个节点都有对应的层高d。

  1. memoryMap[id] == d 表示该节点未分配
  2. memoryMap[id] > d 表示该节点已经被分配过,但是其子节点仍有未分配的
  3. memoryMap[id] = maxOrder + 1 表示超过最大层高(maxOrder默认是11),则说明该节点及其子节点已经被分配完

depthMap

用于存储层高信息,不会发生变化,一般用于通过memoryMapIndex来定位对应的层高

subpages

用于存储被切割的page,该处的SubPage会和Arena的tinySubpagePools、smallSubpagePools关联,并且可用于重组成Page归还到Chunk

PoolSubpage

在Chunk中,最小单位是Page,当Page还需要拆分时,此时就出现了PoolSubPage。每个PoolSubPage按照大小被PoolArena/PoolThreadCache的tinySubpagePools和smallSubpagePools管理着。

  • PoolSubPage的大小是固定的(pageSize),默认是8K。
  • PoolSubPage中的内存块大小都是一样的(elemSize)
    由于内存块可能非常小,所以一个Page可以被切割成非常多的小内存块,于是就需要一个高效的方式(bitmap)来快速定位内存块的使用情况。
  • PoolSubPage是双向节点,所以在tinySubpagePools和smallSubpagePools中就是一个数组加链表的模式存在着

bitmap

bitmap是一个long数组,每个long值的每一位代表着一个内存块的使用情况。由于一个long就64位,而每个PoolSubPage大小固定是8K,所以当存储的是tiny内存块(16B),就有 8*1024/16=512个内存块,512/64=8个long存储,也就是bitMap的长度是8。

所以当要标识一个内存块时,需要知道其在bitMap的哪个long 及 这个long中哪位。

低6位是long中的index,剩余位表示在bitMap的下标。

分配原理

★遵循的原则

1.规格化申请的内存大小,根据内存大小区分当前的需要分配的内存块种类

2.分配内存的优先级

  • 优先从线程的内存缓存中分配

    • 根据大小找到数组下标,然后从queue里面获取内存块
  • 对应种类的内存List分配(tiny/small/qXXX)

    • 根据大小找到数组下标,从head开始找到PoolSubPage/Chunk,PoolSubPage还需要再定位具体的位置(bitMap的下标及long中的位置)
  • 最终方式就是新增一个Chunk
    在这里插入图片描述

PoolThreadCache分配内存

通过allocateTiny,allocateSmall或者allocateNormal分别缓存中的内存。通过下面代码可知,都是先找到一个缓存的内存块,再进行分配。

  1. boolean allocateTiny(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
  2. return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
  3. }
  4. boolean allocateSmall(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
  5. return allocate(cacheForSmall(area, normCapacity), buf, reqCapacity);
  6. }
  7. boolean allocateNormal(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
  8. return allocate(cacheForNormal(area, normCapacity), buf, reqCapacity);
  9. }

那么再看看cacheForXXX系列方法吧。根据下面的代码可以看见,都是先根据normCapacity找到在数组的下标,然后获取缓存数组对应的MemoryRegionCache。

  1. private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) {
  2. //tiny是按照16的大小递增,所以normCapacity >>> 4就是对应下标
  3. int idx = PoolArena.tinyIdx(normCapacity);
  4. if (area.isDirect()) {
  5. return cache(tinySubPageDirectCaches, idx);
  6. }
  7. return cache(tinySubPageHeapCaches, idx);
  8. }
  9. private MemoryRegionCache<?> cacheForSmall(PoolArena<?> area, int normCapacity) {
  10. //small是以512B开始,按照1024B递增,所以通过 normCapacity >>> 10 就可以找到下标
  11. int idx = PoolArena.smallIdx(normCapacity);
  12. if (area.isDirect()) {
  13. return cache(smallSubPageDirectCaches, idx);
  14. }
  15. return cache(smallSubPageHeapCaches, idx);
  16. }
  17. private MemoryRegionCache<?> cacheForNormal(PoolArena<?> area, int normCapacity) {
  18. //normal是从4K开始,大小是2^n,所以计算log2就可以得到下标
  19. if (area.isDirect()) {
  20. int idx = log2(normCapacity >> numShiftsNormalDirect);
  21. return cache(normalDirectCaches, idx);
  22. }
  23. int idx = log2(normCapacity >> numShiftsNormalHeap);
  24. return cache(normalHeapCaches, idx);
  25. }

接下来从这个MemoryRegionCache的queue poll一个内存块(内存块可能为null),再根据MemoryRegionCache的allocate方法分配缓存,并且记录分配次数(如果达到freeSweepAllocationThreshold,需要释放缓存的内存块,让使用率很低的内存块回归Arena,可以使得其他线程使用)。

  1. private boolean allocate(MemoryRegionCache<?> cache, PooledByteBuf buf, int reqCapacity) {
  2. if (cache == null) {
  3. // no cache found so just return false here
  4. return false;
  5. }
  6. boolean allocated = cache.allocate(buf, reqCapacity);
  7. if (++ allocations >= freeSweepAllocationThreshold) {
  8. allocations = 0;
  9. trim();
  10. }
  11. return allocated;
  12. }

再来看看trim方法是如何释放使用率低的缓存内存吧。

  1. //这里直接看io.netty.buffer.PoolThreadCache.MemoryRegionCache#trim就行
  2. public final void trim() {
  3. //计算queue大小和分配次数之差,就能知道有多少都未曾分配过
  4. int free = size - allocations;
  5. allocations = 0;
  6. // 释放掉没有分配过的个数的缓存内存块
  7. if (free > 0) {
  8. //最终调用freeEntry,通过chunk来释放内存
  9. free(free);
  10. }
  11. }

PoolChunk里面分配内存

主要有两个规则,根据大小确定当前是分配小于8K的SubPage还是大于8K的内存

allocate

  1. boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
  2. //handle的低32位表示在memoryMap的位置,高32位表示在SubPage中的位置
  3. final long handle;
  4. if ((normCapacity & subpageOverflowMask) != 0) {
  5. //大于pagesize使用allocateRun分配
  6. handle = allocateRun(normCapacity);
  7. } else {
  8. //小于等于pagesize使用allocateSubpage分配
  9. handle = allocateSubpage(normCapacity);
  10. }
  11. //如果handle小于0说明没有可用的内存了
  12. if (handle < 0) {
  13. return false;
  14. }
  15. ByteBuffer nioBuffer = cachedNioBuffers != null ? cachedNioBuffers.pollLast() : null;
  16. initBuf(buf, nioBuffer, handle, reqCapacity);
  17. return true;
  18. }

allocateRun

  1. private long allocateRun(int normCapacity) {
  2. //计算层级
  3. int d = maxOrder - (log2(normCapacity) - pageShifts);
  4. //找到该层级合适的节点
  5. int id = allocateNode(d);
  6. //小于0说明已经分配完了,没有可用的内存块
  7. if (id < 0) {
  8. return id;
  9. }
  10. freeBytes -= runLength(id);
  11. return id;
  12. }

allocateNode

  1. //d是层高
  2. private int allocateNode(int d) {
  3. int id = 1;
  4. int initial = - (1 << d); // 如果d是2,则 1<<2就是 11111100
  5. byte val = value(id);
  6. if (val > d) { // 如果根节点的value已经大于d,说明没有可用的内存了
  7. return -1;
  8. }
  9. //val小于d:表示一定有可以分配的内存
  10. //id & initial == 0:同层的节点,后续用于遍历
  11. while (val < d || (id & initial) == 0) {
  12. //左子节点
  13. id <<= 1;
  14. val = value(id);
  15. if (val > d) {
  16. //右子节点 异或:相同为0,相异为1,所以和1进行异或,除低1位之外都保留了id的值,而因为id是偶数,低一位为0,所以通过异或1,一定为奇数,得到右子节点
  17. id ^= 1;
  18. val = value(id);
  19. }
  20. }
  21. byte value = value(id);
  22. assert value == d && (id & initial) == 1 << d : String.format("val = %d, id & initial = %d, d = %d",
  23. value, id & initial, d);
  24. setValue(id, unusable); // 标记为不可用
  25. updateParentsAlloc(id); // 更新所有父节点分配信息
  26. return id;
  27. }

allocateSubpage

  1. private long allocateSubpage(int normCapacity) {
  2. //找到arena里面对应大小内存块的头节点
  3. PoolSubpage<T> head = arena.findSubpagePoolHead(normCapacity);
  4. int d = maxOrder; //因为是SubPage,所以层数直接设置为最大层数
  5. synchronized (head) {
  6. //找到memoryMap中的下标
  7. int id = allocateNode(d);
  8. if (id < 0) {
  9. return id;
  10. }
  11. final PoolSubpage<T>[] subpages = this.subpages;
  12. final int pageSize = this.pageSize;
  13. freeBytes -= pageSize;
  14. // 计算SubPage的下标
  15. int subpageIdx = subpageIdx(id);
  16. PoolSubpage<T> subpage = subpages[subpageIdx];
  17. if (subpage == null) {
  18. //初始化一个PoolSubpage
  19. //初始化额外信息,并将此SubPage加入到head的链表中
  20. subpage = new PoolSubpage<T>(head, this, id, runOffset(id), pageSize, normCapacity);
  21. subpages[subpageIdx] = subpage;
  22. } else {
  23. //初始化额外信息,并将此SubPage加入到head的链表中
  24. subpage.init(head, normCapacity);
  25. }
  26. //返回一个long值,高32位是bitmap的数据,低32位是memoryMap的下标
  27. return subpage.allocate();
  28. }
  29. }
PoolSubpage#allocate
  1. long allocate() {
  2. //每个内存块的大小为0,则直接返回一个handle
  3. if (elemSize == 0) {
  4. return toHandle(0);
  5. }
  6. if (numAvail == 0 || !doNotDestroy) {
  7. return -1;
  8. }
  9. //在bitmap中找到有未分配的long
  10. final int bitmapIdx = getNextAvail();
  11. //低6位是long中的index,剩余位表示在bitMap的下标
  12. int q = bitmapIdx >>> 6;
  13. int r = bitmapIdx & 63;
  14. assert (bitmap[q] >>> r & 1) == 0;
  15. //标记该位置内存块已使用
  16. bitmap[q] |= 1L << r;
  17. //如果没有可用的内存块了,直接移除该PoolSubPage
  18. if (-- numAvail == 0) {
  19. removeFromPool();
  20. }
  21. return toHandle(bitmapIdx);
  22. }

回收原理

优先回收到线程的内存缓存中,缓存回收不了就用PoolChunkList来回收,否则最终被释放掉

在这里插入图片描述

PoolThreadCache回收内存

通过add方法,将内存块缓存起来。

  1. boolean add(PoolArena<?> area, PoolChunk chunk, ByteBuffer nioBuffer,
  2. long handle, int normCapacity, SizeClass sizeClass) {
  3. //先根据大小找到不同类型内存块的数组坐标,找到对应大小的MemoryRegionCache
  4. MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass);
  5. if (cache == null) {
  6. return false;
  7. }
  8. //往MemoryRegionCache的queue里面添加内存块
  9. return cache.add(chunk, nioBuffer, handle);
  10. }

通过chunk来释放内存

PoolChunkList#free

  1. boolean free(PoolChunk<T> chunk, long handle, ByteBuffer nioBuffer) {
  2. chunk.free(handle, nioBuffer);
  3. //如果小于最小使用率,需要移除该chunk
  4. if (chunk.usage() < minUsage) {
  5. remove(chunk);
  6. // 添加到preList(PoolChunkList)中,如果没有添加成功此时返回false,会触发该chunk的销毁
  7. return move0(chunk);
  8. }
  9. return true;
  10. }

PoolChunk#free

找到该内存块在memoryMap的下标 及 在bitmap的哪一个long值的哪一位来知道其下标

  1. void free(long handle, ByteBuffer nioBuffer) {
  2. int memoryMapIdx = memoryMapIdx(handle);
  3. int bitmapIdx = bitmapIdx(handle);
  4. // 如果bitmapIdx不为0,说明该内存块是一个SubPage
  5. if (bitmapIdx != 0) {
  6. //获取到对应的SubPage
  7. PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];
  8. assert subpage != null && subpage.doNotDestroy;
  9. //获取该SubPage对应的head节点
  10. PoolSubpage<T> head = arena.findSubpagePoolHead(subpage.elemSize);
  11. synchronized (head) {
  12. //释放结果为false,说明已经从SubPage池移除,此时需要归还到chunk中
  13. if (subpage.free(head, bitmapIdx & 0x3FFFFFFF)) {
  14. return;
  15. }
  16. }
  17. }
  18. //增加空闲空间的计数
  19. freeBytes += runLength(memoryMapIdx);
  20. //更新memoryMap
  21. setValue(memoryMapIdx, depth(memoryMapIdx));
  22. updateParentsFree(memoryMapIdx);
  23. if (nioBuffer != null && cachedNioBuffers != null &&
  24. cachedNioBuffers.size() < PooledByteBufAllocator.DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK) {
  25. cachedNioBuffers.offer(nioBuffer);
  26. }
  27. }

PoolSubpage#free

  1. boolean free(PoolSubpage<T> head, int bitmapIdx) {
  2. if (elemSize == 0) {
  3. return true;
  4. }
  5. int q = bitmapIdx >>> 6;
  6. int r = bitmapIdx & 63;
  7. assert (bitmap[q] >>> r & 1) != 0;
  8. //标记bitmap中该SubPage为未分配状态
  9. bitmap[q] ^= 1L << r;
  10. setNextAvail(bitmapIdx);
  11. //之前被移除掉的SubPage,需要归还到arena SubPage的链表中
  12. if (numAvail ++ == 0) {
  13. addToPool(head);
  14. return true;
  15. }
  16. if (numAvail != maxNumElems) {
  17. return true;
  18. } else {
  19. //如果没有正在使用的内存块了
  20. if (prev == next) {
  21. // 如果只是一个内存块则无需删除该SubPage
  22. return true;
  23. }
  24. // 从SubPage池中移除并归还到正常page
  25. doNotDestroy = false;
  26. removeFromPool();
  27. return false;
  28. }
  29. }

扩充内存块

当向ByteBuffer写数据发现内存块不够用了,会怎么操作呢?

  • 首先得计算出保证需求的内存块大小

    • 对于小于4M的内存申请,则以64B翻倍找到刚好满足需求的大小
    • 对于等于4M的内存申请,则直接返回4M大小
    • 剩余情况,返回需求大小加4M或最大内存大小
  • 然后重新找一个合适大小的内存块,进行标记后,再把数据拷贝过来,释放旧的内存。

    1. void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) {
    2. if (newCapacity < 0 || newCapacity > buf.maxCapacity()) {
    3. throw new IllegalArgumentException("newCapacity: " + newCapacity);
    4. }
    5. int oldCapacity = buf.length;
    6. if (oldCapacity == newCapacity) {
    7. return;
    8. }
    9. PoolChunk<T> oldChunk = buf.chunk;
    10. ByteBuffer oldNioBuffer = buf.tmpNioBuf;
    11. long oldHandle = buf.handle;
    12. T oldMemory = buf.memory;
    13. int oldOffset = buf.offset;
    14. int oldMaxLength = buf.maxLength;
    15. int readerIndex = buf.readerIndex();
    16. int writerIndex = buf.writerIndex();
    17. //分配一个合适大小的内存块
    18. allocate(parent.threadCache(), buf, newCapacity);
    19. //数据拷贝
    20. if (newCapacity > oldCapacity) {
    21. memoryCopy(
    22. oldMemory, oldOffset,
    23. buf.memory, buf.offset, oldCapacity);
    24. } else if (newCapacity < oldCapacity) {
    25. if (readerIndex < newCapacity) {
    26. if (writerIndex > newCapacity) {
    27. writerIndex = newCapacity;
    28. }
    29. memoryCopy(
    30. oldMemory, oldOffset + readerIndex,
    31. buf.memory, buf.offset + readerIndex, writerIndex - readerIndex);
    32. } else {
    33. readerIndex = writerIndex = newCapacity;
    34. }
    35. }
    36. buf.setIndex(readerIndex, writerIndex);
    37. //释放旧的内存
    38. if (freeOldMemory) {
    39. free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache);
    40. }

    }

对比PoolArena和PoolThreadCache

在这里插入图片描述


在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,243人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java内存分配

    栈内存+堆内存 堆内存用来存放由new运算符创建的数组或对象,在堆中分配的内存,由java虚拟机的垃圾回收器来自动管理。 在堆中创建了一个数组或对象后,同时还在栈中定义

    相关 C++内存分配

    1. 栈 编译器自动分配与清除,系统支持 2. 堆 由new分配,释放delete 函数库提供 人工控制,容易产生内存碎片 3. 自由存储区 由mallo

    相关 内存分配

    我们已经知道,每一个进程内存包含多个段,如文本段、已初始化数据段、未初始化数据段和堆栈段等。程序中已初始化的全局变量和静态变量会在已初始化数据段中,而未初始化的数据在未初始化数

    相关 C++内存分配

    C++内存管理: 变量和对象在内存中的分配都是编译器在编译程序时安排好的,但同样带来了不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。动态内存分配解决了这个

    相关 c内存分配

     在任何程序设计环境及语言中,内存管理都十分重要。在目前的计算机系统或 [嵌入式][Link 1]系统中,内存资源仍然是有限的。因此在程序设计中,有效地管理内存资源是程序