8、Netty那些事 - 内存分配入门

309 阅读4分钟

醉卧千山下,风过谢桃花。打不倒我们的会让我们变的更强大,杀不死我们的终究是我们变坚强。

一、概述

今天就让我们正式走入Netty内存管理的世界 ,在正式进行源码解读之前有一些概念和重要属性要给大家介绍下,防止后面看源码的时候发生混乱,Netty为了提高内存的使用效率,引入了Jemalloc内存分配算法,不熟悉Jemalloc内存分配算法的可以看看这篇文章,讲解的很清晰,在看Netty的内存模型时,也可以看到以上算法的实现。好了,下面就让我们进入Netty的内存管理世界吧。

二、内存规格介绍

image.png

Netty内部对内存管理的数据块进行了如下的分类:

  • Tiny 代表 0 ~ 512B 之间的内存块
  • Samll 代表 512B ~ 8K 之间的内存块
  • Normal 代表 8K ~ 16M 的内存块
  • Huge 代表大于 16M 的内存块

在Netty的内部,通过维护SizeClass枚举来描述内存规则

    enum SizeClass {
        Small,
        Normal
    }

当分配内存大于16M时,会直接使用非池化的方式进行内存分配。

三、内存架构设计

image.png 以上就是Netty的内存模型设计,基于此Netty抽象出了一些核心组件如PoolThreadCache、PoolArena、PoolChunk、PoolChunkList、PoolSubpage等。下面就让我们了解下每个组件的功能和作用

3.1. PoolThreadCache

为了避免线程间锁的竞争和同步,每个I/O线程都对应一个PoolThreadCache,负责当前线程使用非大内存的快速申请和释放。当从PoolThreadCache获取不到内存的时候,就从PoolArena的内存池总分配。当内存使用完并释放的时候,会将其放入PoolThreadCache中,方便下次使用。若从PoolArena内存池中获取不到内存的时候,则从堆外内存中申请,申请到的内存叫做PoolChunk。

3.1. PoolArena

通过前文的架构图也能看到,整体分为两部分,一部分是SubPagePools,一部分是PoolChunkList。SubPagePools分为两部分,一部分为tinySubPagePools,用来缓存0~512的PoolSubpage内存块,一部分为smallSubpagePools,用来缓存512~8192的PoolSubpage内存块。

PoolChunkList为PoolChunk链表,PoolArena根据PoolChunk的利用率,把PoolChunk划分到不同的PoolChunkList中去

  • qInit
    • 存储内存利用率为0~25%的PoolChunck,其preList是本身,nextList为q000,当PoolChunck最开始创建的时候,如果内存分配一小于25%,那么即使被完全释放,也不会被回收。
  • q000
    • 存储内存利用率为1%~50%的PoolChunck,其preList是null,nextList为q025,当PoolChunck进入q000列表后,当其内存被完全释放的时候,即内存利用率为0,从q000链表中直接删除,释放物理内存。
  • q025
    • 存储内存利用率为25%~75%的PoolChunck,其preList是q000,nextList为q050,为了防止PoolChunck在临界点来来回在q000和q025之前移动,因此存储范围存在一定的重叠。
  • q050
    • 存储内存利用率为50%~100%的PoolChunck,其preList是q025,nextList为q075,为了提高内存分配成功率,同时让PoolChunck的利用率保持一个较高的水平,PoolArena在分配内存的时候,会从q050链表开始分配,如果从q000开始分配,可能会造成大量的PoolChunck无法回收,造成浪费。
  • q075
    • 存储内存利用率为75%~100%的PoolChunck,其preList是q050,nextList为q100,主要在q050和q100之间做个缓冲,比如内存利用率为100%的PoolChunck被回收了一点内存,不会很快进入q050,否则在q050里选到改chunck时候,可能有满了,来回横跳。
  • q100
    • 内存利用率为100%的chunck,无法再继续分配,只能等待释放。

3.2. PoolSubpage

PoolSubpage用来存储大于512B并且小于8KB的内存分配,之前在PoolArena中其实分为两个数组,分别为inySubpagePools 和 smallSubpagePools 两个数组,高版本已经全部合并到了smallSubpagePools中。

PoolSubpage中每段的内存使用情况会用一个long类型的数组来标记,long类型的存储位数为64B,每一个位用0表示空闲,1标识占用。每次分配内存的时候,可以通过二分查找,即可找到内存在page中的相对偏移量。

四、总结

本篇没怎么分析源码,主要是内存这里涉及到一些比较复杂的流程,先给大家介绍下相关概念,对Netty的内存管理中涉及到的一些组件有一些大概的了解,后面将会带着大家一起学习下相关源码设计。