1 降低对象大小
能用基本类型就不用包装类
懂得都懂。
应该定义成类变量的不要定义为实例变量
- 一个类 =》 一个类变量
- 一个实例 =》一个实例变量
- 一个类 =》 多个实例
- 实例越多,浪费越多
当然 netty 会结合这两点!
2 预估分配的内存
- 对已可预知固定size的HashMap避免扩容
提前计算好初始size或者直接使用
com.google.common.collect.Maps#newHashMapWithExpectedSize
- 根据接受到的数据动态调整(guess) 下个要分配的Buffer的大小,比如
io.netty.channel.AdaptiveRecvByteBufAllocator
3 零拷贝
逻辑组合代替实际复制
- CompositeByteBuf
包装替代实际复制
JDK的零拷贝接口
Netty 通过在 DefaultFileRegion 包装了 NIO 的 FileChannel.transferTo() 实现零拷贝:іо.nеttу.сhаnnеl.DеfаultFіlеRеgіоn#trаnѕfеrТо
4 堆外内存
堆外内存生活场景:
烧烤店热季时人满为患,店家就会在门口加摆一些桌子招待新客人。
店内 =》 JVM内部 =》堆(heap) +非堆(non heap)
店外 =》 JVM外部 =》堆外(off heap)
优点
- 更广阔的“空间”,缓解店铺内压力->破除堆空间限制,减轻GC压力
- 减少“冗余”细节(烧烤过程是在室外进行的:烤好直接上桌 V.S 烤好还要
进店内) =》避免复制
缺点
- 需要搬桌子 =》创建速度稍慢
- 受城管监管、风险大 =》堆外内存受 os 管理
源码
- 堆外内存/堆内内存的默认选择
- 切换方式
堆外内存的分配本质
- 可见依旧调用的 JDK 原生的堆外内存实现。
5 内存池
内存池生活场景:
点菜单的演进
一张纸:一桌客人一张纸
点菜平板:循环使用
为何使用内存池
- 创建对象开销大
比如堆外内存就是这样 - 对象高频率创建且可复用
- 支持并发又能保护系统
- 维护、共享有限的资源
实现内存池
- 开源实现:Apache Commons Pool
程序共享,内存竞争较严重 - Netty轻量级对象池实现io.netty.util.Recycler
基于特定场景才创建
内存池/非内存池的配置切换
根据参数配置判定(切换方式二)