前言
Netty所谓的池化就是先申请了一块大内存,后面需要分配的时候就来我这里分就完了。以堆外直接内存分配为例,Netty以Chunk为单位16M申请了一块连续内存,这么一大块内存是以平衡二叉树的形式组织起来的。分配的时候就从这颗树上找合适的节点。池化内存的分配是Netty的最为核心部分,这块的代码很多位运算,不太容易看懂,读的时候需要边调试边分析。
平衡二叉树
Netty使用平衡二叉树将申请到的Chunk块组织起来,如下图所示,并使用数组将整个树映射进去,见下文构造函数中memoryMap。

大家好,我是老梁,一个想跟大伙分享点干货的家伙。
到了一周中最放松的时刻,回来就10点多了,洗洗涮涮就到了11点了。
夜神人静了,喝了点碧螺春,老梁坐在沙发上抱着电脑跟大伙聊聊天。
今天聊聊居转户,申请过程中也是各种查询挺费劲,记录下或许对一些朋友有点用。
在前面文章『Netty12# 池化内存框架流程』Netty会将不同的内存尺寸缓存起来,每个线程绑定了专属逻辑内存区域(PoolArena),减少资源竞争。每个线程绑定了缓存PoolThreadCache,内存分配时,先从当前线程绑定的PoolThreadCache缓存分配。下图为涉及到相关类的关系图:
.png)
工作过程:
@1 通过引导类传入NioEventLoopGroup,线程工厂创建的线程均为FastThreadLocalThread
@2 FastThreadLocalThread持有InternalThreadLocalMap(内部维护一个对象数组)
@3 当通过PooledByteBufAllocator#newDirectBuffer分配内存时,通过调用PoolThreadLocalCache#get()完成对InternalThreadLocalMap的第一次填充,对象数组下标为线程索引号,其对应的值为PoolThreadCache。
@4 PoolThreadCache是被当前线程缓存的对象
大家好,我是老梁,一个想跟大伙分享点干货的家伙。
明天请假办点事,夜神人静了,喝了点白开水,到了一周中最放松的时候,老梁确放松不起来。
丢掉幻想,准备战斗!这句常见于官方辞令,老梁觉得用到个人身上也不违和,朗朗上口的,那就拿来用用。
那就最近也发生的事情和想法聊一下。不一定对,各位随便看看。
title:Q1# 问题整理20210529
categories:Questions
tags:Questions
date:2021-05-29 11:55:01
业务要发展,功能要增强,基础设施要完善,变更就无法避免,带来新功能的同时也带来了风险。整理下近期碰到的和被问的几个问题。
Zookeeper几乎每年都能听到有公司踩到坑,故事往往是这样:@1 多个业务线共用zk集群,某个新上的功能把zk当缓存用,大量创建zk路径,造成zk不堪重负瘫痪;@2 某个新上的功能往zk集群中写入过大消息,单条消息好几兆,造成zk集群性能下降,甚至带宽、磁盘被打满。
知道可能的坑咱肯定不能再去踩了,重要中间件的独立zk集群彻底隔离,上面的问题基本可以避免。今天的故事却发生在watch的数量上。
现象
从4月中旬左右发现zk的watch水位在翻倍增长,从200多万到400多万一直翻到800多万,一直到五一之后的12000万。watch水位的升高会影响与zk通信的延迟。

原因
SDK中有使用一个公共主题用于客户端行为收集,该主题下会被注册临时节点,临时节点会被watch用于一个特定功能。在卡点升级过程中随着服务应用的接入,注册的节点越来越多。例如:3000个临时节点每个节点都会有3000个watch数量,就会有3000的平方个watch数量,就是900万个watch数量
解决
止血方案: 删除公共主题下的临时节点,watch数量水位恢复正常
根除方案:剔除SDK中对注册节点的watch,例如:curator中用NodeCacheListener替代TreeCacheListener,同时移除关联节点的wath功能,或者对公共主题进行过滤,不再注册节点
PooledByteBufAllocator作为池化内存分配的入口,提供了众多的配置参数和便捷方法。这篇主要撸下他们大体都啥含义、干啥用的。为后面池化内存其他组件做铺垫。
下面的成员变量基本都提供了默认值,可以通过参数去自定义,下面表格给出具体说明。
| 成员变量 | 说明 |
|---|---|
| DEFAULT_NUM_HEAP_ARENA | PoolAreana(堆内存)个数,默认为核数的2倍,可以由参数-Dio.netty.allocator.numHeapArenas指定 |
| DEFAULT_NUM_DIRECT_ARENA | PoolAreana(堆外内存)个数默认为核数的2倍,堆外内存,可以通过-Dio.netty.allocator.numDirectArenas指定 |
| DEFAULT_PAGE_SIZE | 默认pageSize=8K,可以通过-Dio.netty.allocator.pageSize,需大于4096且为2的倍数 |
| DEFAULT_MAX_ORDER | 二叉树最高层数,取值范围为0~14,默认为11,可以通过-Dio.netty.allocator.maxOrder参数指定 |
| DEFAULT_TINY_CACHE_SIZE | 默认tiny类型缓存池大小512,可以通过-Dio.netty.allocator.tinyCacheSize指定 |
| DEFAULT_SMALL_CACHE_SIZE | 默认small类型缓存池大小为256,可以通过-Dio.netty.allocator.smallCacheSize指定 |
| DEFAULT_NORMAL_CACHE_SIZE | 默认normal类型缓存池大小为64,可以通过-Dio.netty.allocator.normalCacheSize指定 |
| DEFAULT_MAX_CACHED_BUFFER_CAPACITY | 默认为32KB,用于限制normal缓存数组的长度,可以通过-Dio.netty.allocator.maxCachedBufferCapacity指定 |
| DEFAULT_CACHE_TRIM_INTERVAL | 默认8192,分配次数阈值,超过后释放内存池,可以通过-Dio.netty.allocator.cacheTrimInterval指定 |
| DEFAULT_CACHE_TRIM_INTERVAL_MILLIS | 默认0不开启,定时释放内存池,可以通过-Dio.netty.allocator.cacheTrimIntervalMillis指定 |
| DEFAULT_USE_CACHE_FOR_ALL_THREADS | 默认true,使用线程缓存,可以通过-Dio.netty.allocator.useCacheForAllThread制定 |
| DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT | 直接内存的校准对齐参数,分配内存时按位与(&)校准。默认0不校准,可以通过-Dio.netty.allocator.directMemoryCacheAlignment指定 |
| DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK | 默认1023,指定PoolChunk缓存ByteBuffer对象的最大数量,可以通过-Dio.netty.allocator.maxCachedByteBuffersPerChunk指定 |
| MIN_PAGE_SIZE | 校验用的,PageSize不能小于4KB |
| MAX_CHUNK_SIZE | 校验用的,Chunk的边界值,(((long) Integer.MAX_VALUE + 1) / 2) |
| heapArenas | Arena数组,元素为HeapArena |
| directArenas | Arena数组,元素为DirectArena |
| PooledByteBufAllocatorMetric metric | 暴露统计指标,例如:用了多少堆内存、用了多少堆外直接内存等 |