netty源码浅析-池化内存PoolThreadCache分配

keil+Jlink GD32调试记录 Jlink+keil,GD32调试记录,踩坑及解决,持续更新。 阅读详情
PoolThreadCache分配

上面我们已经分析了没有命中缓存的分配方式,会分配一段连续的内存来分配,现在我们来分析一下名字缓存的分配方式,这个实现就在PoolThreadCache中。我们先看看这个类的几个属性

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;

这个就是缓存数组,我们可以看到这分为两类缓存,直接内存的和堆内存的缓存,而这里我们以直接内存分析,又可以分为了三种类型,tinySubPageDirectCaches、smallSubPageDirectCaches、normalDirectCaches。这三种类型代表了不同的内存大小缓存,我们来看下他们是如何创建的

if (directArena != null) {
            tinySubPageDirectCaches = createSubPageCaches(
                    tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);//512 32 SubPageMemoryRegionCache(512,tiny)[32]
            smallSubPageDirectCaches = createSubPageCaches(
                    smallCacheSize, directArena.numSmallSubpagePools, SizeClass.Small);//256 4 SubPageMemoryRegionCache(256,small)[4]

            numShiftsNormalDirect = log2(directArena.pageSize);
            normalDirectCaches = createNormalCaches(
                    normalCacheSize, maxCachedBufferCapacity, directArena);//64  32K NormalMemoryRegionCache(64)[3]

            directArena.numThreadCaches.getAndIncrement();//持有该arena的线程数
        }

我们进入createSubPageCaches方法

private static <T> MemoryRegionCache<T>[] createSubPageCaches(
            int cacheSize, int numCaches, SizeClass sizeClass) {
        if (cacheSize > 0 && numCaches > 0) {
            @SuppressWarnings("unchecked")
            MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];//32
            for (int i = 0; i < cache.length; i++) {
                // TODO: maybe use cacheSize / cache.length
                cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);//SubPageMemoryRegionCache ->
            }
            return cache;
        } else {
            return null;
        }
    }

这里如果是tiny类型就创建了32个MemoryRegionCache,并且将他们初始化,我们跟进SubPageMemoryRegionCache的创建

SubPageMemoryRegionCache(int size, SizeClass sizeClass) {
            super(size, sizeClass);
        }
MemoryRegionCache(int size, SizeClass sizeClass) {
            this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);//获取大于size距离最近的2的次幂数值 ,这里传入的是512
            queue = PlatformDependent.newFixedMpscQueue(this.size);
            this.sizeClass = sizeClass;
        }

我们可以看到这里如果是tiny类型,则创建了一个长度为512的MpscArrayQueue队列,并且赋值了是tiny类型。small类型的创建tiny创建类似,只是创建的数组长度为4个,队列的长度为256。我们看下normal类型的创建

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);//log2(32K/8192) + 1 = 3

            @SuppressWarnings("unchecked")
            MemoryRegionCache<T>[] cache = new MemoryRegionCache[arraySize];
            for (int i = 0; i < cache.length; i++) {
                cache[i] = new NormalMemoryRegionCache<T>(cacheSize);
            }
            return cache;
        } else {
            return null;
        }
    }

创建了一个长度为3的NormalMemoryRegionCache数组,数组内的MemoryRegionCache内创建长度为64的队列,类型为Normal。这里我们看下分配后的大小分类说明

  • tiny:总共有32个规格,都是16的整数倍,从0B、16B、32B…496B
  • small:总共有四个规则,512B、1024B、2048B、4096B
  • normal:总共有三个规格,8K、16K、32K

也就是不同规格释放后的byteBuf会放入到数组对应位置的MemoryRegionCache的队列中,等下次使用时直接取出

我们回到上面分析过得分配内存过程,回到io.netty.buffer.PooledByteBufAllocator#newDirectBuffer方法

protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        //每个线程都有一个threadCache,第一次执行会在这里初始化
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        final ByteBuf buf;
        if (directArena != null) {
            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()方法获取PoolThreadCache,我们看到threadCache就是PoolThreadLocalCache,我们先看下这个类

final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache>

这个类继承了FastThreadLocal这个是netty对threadLocal的重写然后调用get方法,从线程本地获取如果获取不到就创建一个会执行initialValue方法

protected synchronized PoolThreadCache initialValue() {
            final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);//共享线程使用最少的领域
            final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);

            final Thread current = Thread.currentThread();
            if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
                final PoolThreadCache cache = new PoolThreadCache(
                        heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
                        DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);//512 256 64 32K 8K

                if (DEFAULT_CACHE_TRIM_INTERVAL_MILLIS > 0) {
                    final EventExecutor executor = ThreadExecutorMap.currentExecutor();
                    if (executor != null) {
                        executor.scheduleAtFixedRate(trimTask, DEFAULT_CACHE_TRIM_INTERVAL_MILLIS,
                                DEFAULT_CACHE_TRIM_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
                    }
                }
                return cache;
            }
            // No caching so just use 0 as sizes.
            return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
        }

这里会先选出使用最少的poolArean,然后根据是不是为所有线程共享缓存或者是当前线程是不是FastThreadLocalThread类型创建不同的PoolThreadCache,表示是不是启用缓存。我们继续回到newDirectBuffer方法,我们上面已经分析过,这个方法会来到io.netty.buffer.PoolArena#allocate()中

private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        //规范后值
        final int normCapacity = normalizeCapacity(reqCapacity);//272
        if (isTinyOrSmall(normCapacity)) { // 需要申请的大小是否小于8K
            int tableIdx;
            PoolSubpage<T>[] table;
            //是不是tiny类型
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
                //先从缓存中分配内存,分配成功直接返回
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {// 256 272
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = tinyIdx(normCapacity);//7
                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;
            }
            //tiny类型table中分配normCapacity的位置
            final PoolSubpage<T> head = table[tableIdx];//table[17]

            /**
             * 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) {
                //第一次分配双向链表中就一个节点,head.next就是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;
                }
            }
            //创建新的PoolChunk分配
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
            }

            incTinySmallAllocation(tiny);
            return;
        }
        //分配norm类型,8K-16M
        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
            //分配huge对象,超过了16M
            allocateHuge(buf, reqCapacity);
        }
    }

这个方法我们在上面已经分析过了,但是当时跳过了缓存分配的方式,我们现在继续分析,我们知道每次分配的时候都会先从缓存分配,如果缓存无法分配了再从poolChunkList分配,如果也无法分配就新创建一个poolChunk来分配。这里我们跟进到tiny类型缓存分配的方法中

if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {// 256 272      
        return;
   }

boolean allocateTiny(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
        return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
    }

我们继续跟进先看看cacheForTiny方法

 private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) {
        int idx = PoolArena.tinyIdx(normCapacity);//除以16,查看从哪个位置分配
        //如果是直接内存
        if (area.isDirect()) {
            return cache(tinySubPageDirectCaches, idx);
        }
        return cache(tinySubPageHeapCaches, idx);
    }

private static <T> MemoryRegionCache<T> cache(MemoryRegionCache<T>[] cache, int idx) {
        if (cache == null || idx > cache.length - 1) {
            return null;
        }
        //获取数组对应下标的MemoryRegionCache
        return cache[idx];
    }

这里可以看到获取了缓存数组中对应位置的MemoryRegionCache,然后继续回到allocateTiny方法

private boolean allocate(MemoryRegionCache<?> cache, PooledByteBuf buf, int reqCapacity) {
        //非空判断
        if (cache == null) {
            // no cache found so just return false here
            return false;
        }
        //执行分配
        boolean allocated = cache.allocate(buf, reqCapacity);
    	//达到了阈值尝试释放一次
        if (++ allocations >= freeSweepAllocationThreshold) {
            allocations = 0;
            trim();
        }
        return allocated;
    }

这里先判断cache不能为null,然后开始执行申请操作

public final boolean allocate(PooledByteBuf<T> buf, int reqCapacity) {
            //队列刚初始化,这里就是获取不到内容
            Entry<T> entry = queue.poll();
            if (entry == null) {
                return false;
            }
    //初始化操作
            initBuf(entry.chunk, entry.nioBuffer, entry.handle, buf, reqCapacity);
            entry.recycle();

            // allocations is not thread-safe which is fine as this is only called from the same thread all time.
            ++ allocations;
            return true;
        }

这里会从MemoryRegionCache的队列中获取一个entry对象,我们看下这个这个entry类内部结构

static final class Entry<T> {
            final Handle<Entry<?>> recyclerHandle;
            PoolChunk<T> chunk;//属于哪个chunk
            ByteBuffer nioBuffer;//内存
            long handle = -1;//唯一确定chunk中的一段内存
            ......
}

然后回到allocate方法,这里判断如果为null,直接返回false,也就是cache分配失败。哪这个queue什么时候有值呢,其实我们在上面分析内存回收的时候有分析过我们在回顾一下io.netty.buffer.PoolArena#free方法

void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) {
        //如果是非池化的
        if (chunk.unpooled) {
            int size = chunk.chunkSize();
            //释放内存
            destroyChunk(chunk);
            activeBytesHuge.add(-size);
            deallocationsHuge.increment();
        } else {
            //如果是池化的查看是什么类型
            SizeClass sizeClass = sizeClass(normCapacity);
            //放入队列中下次继续复用
            if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {
                // cached so not free it.
                return;
            }
            //如果队列满了或者其他原因导致加入队列失败则标记这个内存未被使用
            freeChunk(chunk, handle, sizeClass, nioBuffer, false);
        }
    }

 boolean add(PoolArena<?> area, PoolChunk chunk, ByteBuffer nioBuffer,
                long handle, int normCapacity, SizeClass sizeClass) {
        //找到对应分配大小的位置
        MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass);
        if (cache == null) {
            return false;
        }
        //添加到队列中,下次申请同样大小的内存时直接使用
        return cache.add(chunk, nioBuffer, handle);
    }

public final boolean add(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle) {
            //从对象池中获取一个entry,并且将所有的chunk、内存对象和handle信息进行赋值
            Entry<T> entry = newEntry(chunk, nioBuffer, handle);
            //加入队列中
            boolean queued = queue.offer(entry);
            //如果放入失败将entry回收
            if (!queued) {
                // If it was not possible to cache the chunk, immediately recycle the entry
                entry.recycle();
            }

            return queued;
        }

有一段关键性代码,就是在释放内存的时候先尝试添加到cache中对应MemoryRegionCache的队列中

我们继续回到io.netty.buffer.PoolThreadCache.MemoryRegionCache#allocate方法,这里返回了一个entry对象并且不为null,下面开始执行初始化操作,如果是tiny或者small类型

protected void initBuf(
                PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf<T> buf, int reqCapacity) {
            chunk.initBufWithSubpage(buf, nioBuffer, handle, reqCapacity);
        }

 void initBufWithSubpage(PooledByteBuf<T> buf, ByteBuffer nioBuffer, long handle, int reqCapacity) {
        initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx(handle), reqCapacity);
    }

 private void initBufWithSubpage(PooledByteBuf<T> buf, ByteBuffer nioBuffer,
                                    long handle, int bitmapIdx, int reqCapacity) {
        assert bitmapIdx != 0;

        int memoryMapIdx = memoryMapIdx(handle);//二叉树中分配的哪个page

        PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];//获取子page
        assert subpage.doNotDestroy;
        assert reqCapacity <= subpage.elemSize;

        buf.init(
            this, nioBuffer, handle,
            runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize + offset,
                //runOffset(memoryMapIdx): page在chunk中的偏移量
                //(bitmapIdx & 0x3FFFFFFF): 子page是属于第几个子page
                //(bitmapIdx & 0x3FFFFFFF) * subpage.elemSize + offset:表示在当前page的偏移量
                //offset:0
                reqCapacity, subpage.elemSize, arena.parent.threadCache());
    }

这里如果是normal类型

void initBuf(PooledByteBuf<T> buf, ByteBuffer nioBuffer, long handle, int reqCapacity) {
        int memoryMapIdx = memoryMapIdx(handle);//取低32位
        int bitmapIdx = bitmapIdx(handle);//取高32位
        if (bitmapIdx == 0) {//如果分配的内存大于一个page
            byte val = value(memoryMapIdx);
            assert val == unusable : String.valueOf(val);
            buf.init(this, nioBuffer, handle, runOffset(memoryMapIdx) + offset,
                    reqCapacity, runLength(memoryMapIdx), arena.parent.threadCache());
        } else {
            initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx, reqCapacity);
        }
    }

这些初始化方法我们在上面已经分析过了,这里就不再分了

我们继续回到io.netty.buffer.PoolThreadCache.MemoryRegionCache#allocate方法,初始化结束后将entry对象回收,这个是如果不回收,弹出的对象会在GC的时候被回收掉,然后判断是不是到了freeSweepAllocationThreshold阈值默认是8K,如果是尝试释放内存,然后直接返回,申请内存成功。可能会有同学有疑问为什么PoolArena有长度为32的tinySubpagePools和长度为4的smallSubpagePools,而cache中也有。其实PoolArena中的中的数组,如tinySubpagePools中的元素其实也是双向链表,他会把我们第一次申请的元素连接到数组中的head下,这样我们如果在下次需要申请和之前申请的长度相同时,就可以快速找到相同大小的已经分配的poolChunk,然后就可以直接通过这个page已经切分后的subpage进行分配了。而cache中的就是缓存的MemoryRegionCache数组,里面装着已经被回收可以复用的内存。

到这里池化分配我们就分析结束了。

5G无线关键技术 — 新型传输波形技术和先进编码与调制技术 目录 1、OFDM传输波形技术 2、滤波器组多载波 (Filterbank multicarrier:FBMC) 3、先进编码与调制技术 4、空间调制(Spatial Modulation SM) 5、频率正交幅度调制 1、OFDM传输波形技术 OFDM是当前Wi-Fi和LTE标准中的高速无线通信的主要传信模式。 LTECP配置 子载波间隔 CP长度Tcp 有用符号长度Tu CP比例.. 阅读详情

相关推荐

ubuntu20.04安装nvidia-docker(解决有 Release 文件。 N: 无法安全地用该源进行更新,所以默认禁用该源错误)

然后想都不要想,nvidia+linux安装的东西,那肯定是不能安装成功的,恶毒的nvidia。按照nvidia官网给出的安装nvidia-docker流程来。在sudo apt-get update出现报错。然后再次update,发现还是会有一些警告类似于。,直接点击网页手动下载,发现下载成功!,注意会有安装先后顺序,安装给的提示就行。最后输入命令发现安装成功。然后替换掉里面的内容。直接不管,直接上命令。

weixin_48144140的博客 1235

死磕Netty源码之内存分配详解(三)PoolThreadCache线程缓存内存分配

内存分配 关于我:http://huangth.com GitHub地址:https://github.com/RobertoHuang 免责声明:本系列博客并非原创,主要借鉴和抄袭闪电侠,占小狼等知名博主博客。如有侵权请及时联系 线程私有分配 在介绍PoolArena内存分配结构分析的时候提到内存分配会先从线程缓存里分配,这个线程缓存其实就是PoolThre...

黄太洪的博客 2480

skywalking各项指标说明

skywalking各项指标说明

没有简介 2487

dump排查内存异常 java_记一次生产环境netty堆外内存占用异常排查

外网linux centos 下 ,java8 ,然后用的netty4.1.18版本,在线玩家非常少,而且日志中还记录到出现过DirectMemory oom 也就是实际占用内存大于过4gtop命令显示占用了3.1g内存实际上启动参数配置的:Xmx2048m -XX:MaxDirectMemorySize=2048, 通过jmap -heap命令打印如下:top命令显示内存占用打印的java进程启...

weixin_39670545的博客 2148

深入解析璞致PZ-ZU47DR RFSoC核心板:硬件架构与多通道ADC/DAC设计实战

本文深入解析璞致PZ-ZU47DR RFSoC核心板的硬件架构与多通道ADC/DAC设计实战。作为Xilinx Zynq UltraScale+ RFSoC系列的代表产品,PZ-ZU47DR集成了8通道5GSPS ADC和8通道9.85GSPS DAC,支持直接射频采样,大幅简化射频链路设计。文章详细介绍了其技术优势、设计挑战及实战技巧,为5G小基站、毫米波等应用场景提供高效解决方案。

meat5的博客 830

第10篇:Agent + 爬虫 = 全自动数据采集专家

在企业级数据采集场景中,传统爬虫的痛点已经到了无法忽视的地步:而Agent + 爬虫的组合,就是这些痛点的终极解决方案。它的核心逻辑是:把爬虫从「人工写死的固定执行脚本」,升级为「能自主分析、自主决策、自主适配、自主修复的智能采集机器人」。我们在生产环境中做过对比测试:传统爬虫开发10个网站的采集脚本需要3人天,而Agent采集机器人只需要3分钟,页面改版后的修复成本从1人天降到了0,采集任务的成功率从62%提升到了95%以上。终端执行以下命令,安装基础依赖,与前9篇环境完全兼容: 二、核心能力拆解:Age

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1576

内存占用高问题排查思路

可能性一:突发大批数据操作。使用消息队列等方式削峰。可能性二:缓慢内存泄漏。

予早随笔 383

NVIDIA GR00T SONIC - 人形机器人强化学习全身控制入门教程(一)

SONIC 是一个类人行为基础模型,它为机器人提供了一套从大规模人类动作数据中学习而来的核心运动技能。与为每个动作单独构建控制器不同,SONIC 将动作追踪作为可扩展的训练任务,因此单一套统一策略即可产生自然的全身运动,并支持多种多样的行为。         🎯 主要特点: 🚶 自然的全身运动(行走、爬行、动态动作) 🎮 支持实时 VR 遥操作 🤖 高级规划与交互的基础 📦 即用型 C++ 推理栈

2506_90492529的博客 837

Netty学习——源码篇12 Netty池化内存管理机制

首先调用leastUsedArena()方法分别获得类型为PoolArena的heapArena和directArena对象。那么heapArena和directArena对象是在哪里初始化呢?其实就是创建了一个固定大小的PoolArena数组,数组大小由传入的参数nHeapArena和nDirectArena决定。发现nHeapArena和nDirectArena是通过DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA 这两个常量默认赋值的。

geminigoth的博客 2380

【Bug已解决】Codex capacity errors 自动重试与意图保留 解决方案

原始报错:Codex Desktop: capacity errors should preserve intent and auto-retry instead of handing model routing back to the user场景:向模型服务发请求时遇到容量错误(服务端繁忙 / 限流 / 容量不足),客户端不做任何重试,直接把"请求失败,请选择另一个模型或重试"的锅甩给用户,让用户手动去点。用户本来只是想完成一次任务,却被中途打断去当"调度员"。

博客 277

LPDDR5 学习笔记01:功能概述

LPDDR5/LPDDR5X SDRAM设备采用8/16DQ通道架构,支持2Gb-32Gb容量。通过CK命令时钟和更快的WCK数据时钟协同工作,7个CA引脚传输控制信息,支持2:1或4:1的WCK:CK比例。设备提供BG/8B/16B三种存储模式选择,分别支持不同数据速率(最高8.533Gbps)和突发长度。采用0.5V VDDQ电源电压,实现高速双倍数据速率传输,并通过差分WCK时钟完成数据采样。读写采用突发访问机制,需先激活命令再执行读写操作。新型LPDDR5X仅支持BG和16B模式,优化了高速性能。

一只豌豆象的博客 2197

吃透Netty源码系列二十九之池化内存分配总结

吃透Netty源码系列二十九之池化内存分配总结申请缓冲区创建PoolThreadLocalCache创建PoolThreadCache获取线程本地变量的PoolArena进行分配尝试用缓存来分配缓存分配失败,走正常分配流程释放缓冲区创建实体放入队列释放块防止内存泄露的措施 申请缓冲区 今天在来总结下内存分配中的几个主要的角色。这个就是我们用的池化内存分配器,里面会进行很多参数的配置,这些参数的默认...

王伟王胖胖的博客 961

HFSS实战项目——GPS北斗双模微带天线

本文设计了一款方形圆极化微带天线应用于北斗B1和GPSL1频段,满足高增益、低VSWR、适当方向图等要求。设计中心频率为1568MHz,介质板采用厚度h=1.6mm的Arlon AD450(tm)板,相对介电常数为4.5,采用探针馈电。

2401_87464239的博客 1766

Netty学习——源码篇12 Netty池化内存管理机制 备份

现在来分析池化内存的分配原理。首先找到AbstractByteBufAllocator的子类PooledByteBufAllocator实现分配内存的两个方法:newDirectBuffer和newHeapBuffer方法。观察发现,这两个方法大体结构是一样的,以newDirectBuffer为例,简单分析一下。首先,通过threadCache.get()方法获得一个类型为PoolThreadCache的cache对象,然后,通过cache获得directArena对象;

geminigoth的博客 946

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Netty内存分配

通过PooledByteBufAllocator进入PoolArena尝试分配内存,每个线程在第一次分配内存的时候从Arena数组中选择一个固定的Arena并绑定,在整个生命周期都只与该Arena交互。

Victorgcx的博客 1141

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Netty学习——源码篇13 命中缓存的分配

以 tinySubPageDirectCaches[1]为例(下标选择1是因为下标为0代表的规格是0Byte,其实就代表一个空的缓存),在tinySubPageDirectCaches[1]的缓存对象中所缓存的ByteBuf的缓冲区大小是16Byte,在tinySubPageDirectCaches[2]中缓存的ByteBuf的大小为32Byte,以此类推,tinySubPageDirectCaches[31]中缓存的ByteBuf大小事496Byte。具体类型规则的配置如下。现在分析命中缓存的相关逻辑。

geminigoth的博客 1421

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Netty4核心原理⑭】【Netty 内存分配 ByteBuf❷】

本系列虽说本意是作为 《Netty4 核心原理》一书的读书笔记,但在实际阅读记录过程中加入了大量个人阅读的理解和内容,因此对书中内容存在大量删改。第十一章 Netty 内存分配 ByteBuf</</</</【Netty4核心原理】【全系列文章目录】在【Netty4核心原理⑬】【Netty 内存分配 ByteBuf❶】

猫吻鱼的博客 934

netty源码浅析-池化内存直接申请

这里会赋值当前的poolSubpage是属于那个chunk,然后赋值内存分配管理数组下标位置,在这个chunk中的内存起始位置,赋值page的大小,然后创建一个长度为8的位图。比如这里我们是一颗树高度为2,就是[0,0,1,1,2,2,2,2],我们将下标找到这个最左侧的节点,也就是第一个2的位置将其变为了12,然后向上循环修改后的数组为[0,1,2,1,12,2,2,2],这样也就是说明跟节点能分配的大小为8M,他的左子节点能分配的大小为2M,右子节点可以分配4M大小。我们再次回到上面的申请方法。

GeekerJava的博客 226

Netty源码分析----PoolThreadCache

整体介绍 在讲PoolArena分配的时候,有说到会先从线程缓存里分配,这个线程缓存其实就是PoolThreadCache(好了,从这里开始,要开始出现几个名字差不多的东西….别弄混淆了….(T▽T)),Netty从几个方面去减少线程之间的竞争,一个是在PooledByteBufAllocator持有多个PoolArena,使用线程相关的cache,通过这两种方法去减少竞争,当然其中会带来复杂度...

菜鸟一名 554

Netty内存池 (5w长文+史上最全)

文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 经典图书:《Java高并发核心编程(卷1)》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷2)》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Netty Zookeeper Redis 高并发实战》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《

架构师尼恩 7735

Netty 的 内存池 是如何实现的

对于今天的源码剖析,你可以带着下面这么几个问题: 1 PoolArena中的PoolSubpage数组和PoolChunk中的PoolSubpage数组有什么关联? 2 PoolThreadCache中的MemoryRegionCache数组与PoolSubpage是否有联系? 3 Netty内存池的整体结构是什么样的? 4 如果让你来介绍Netty内存池,你如何来描述? 内存池源码剖析 调试用例 上一节,我们说了Netty根据请求的大小将其分成四类:Tiny、Small、Normal、Huge,这四类请求

炫的博客 2万+

Netty源码分析-PoolThreadCache

final class PoolThreadCache { final PoolArena<byte[]> heapArena; final PoolArena<ByteBuffer> directArena; //分别存储堆外和堆内的内存,MemoryRegionCache内部结构是链表 private final Me...

大新博客 599

netty源码浅析-池化内存回收

这里根据bitmapIdx计算在位图数组中的位置,然后将bit为重新设置为0,也就是没有被占用状态,然后设置下一个可用位置就是当前释放的位置,如果可用大小和最大可分配内容不相同则直接返回,说明这个poolSubpage还在被占用,如果相等,查看是不是就一个head节点,如果是也直接返回,如果不是可能是别的poolSubpage加入了pool,则将释放的subpage移除。直接内存通过两种方式释放堆外内存,一种是使用cleaner执行clean方法释放内存,一种是执行unsafe方法释放内存。

GeekerJava的博客 341

吃透Netty源码系列二十四之池化内存分配五

吃透Netty源码系列二十四之池化内存分配五PoolChunk的initBuf PoolChunk的initBuf 上一篇我们讲到子页的内存分配,讲到了我们获取了handle,今天将获取之后做什么。 我们可以看到子页分配的都是那么大的数,原因上篇已经讲过了。后面会有获取nioBuffer,就是在 ...

王伟王胖胖的博客 881

谈一谈 Netty 的内存管理 —— 且看 Netty 如何实现 Java 版的 Jemalloc

深度剖析 Linux 伙伴系统的设计与实现》《细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现》《深度解读 Linux 内核级通用内存池 —— kmalloc 体系》好了,今天的内容就到这里,我们下篇文章见~~~

bin的技术小屋 1223

Netty学习——源码篇13 命中缓存的分配 备份

以 tinySubPageDirectCaches[1]为例(下标选择1是因为下标为0代表的规格是0Byte,其实就代表一个空的缓存),在tinySubPageDirectCaches[1]的缓存对象中所缓存的ByteBuf的缓冲区大小是16Byte,在tinySubPageDirectCaches[2]中缓存的ByteBuf的大小为32Byte,以此类推,tinySubPageDirectCaches[31]中缓存的ByteBuf大小事496Byte。具体类型规则的配置如下。现在分析命中缓存的相关逻辑。

geminigoth的博客 784
上一篇: netty源码浅析-PooledByteBuf扩容操作
下一篇: sentinel源码分析-01客户端启动和注册
胖柯G
博客等级 码龄10年 453粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值