内存池模型
同样的,getty跟netty一样,引入了一套内存池管理以置于为框架提供良好的性能支持。
getty的内存池结合了kafka和netty内存池的部分思想。最终融合成为getty现在的内存池实现。内存池在初始化时会根据配置设置一块总的内存空间大小,这块内存空间可以是堆内内存也可以是堆外内存。内存一开始不会真的开辟,只有需要用到的时候才会真正的从内存中分配出来,这里的好处是避免一开始就占用系统的内存空间。
内存池内划分为两大区域,一是剩余可分配的内存空间,二是已经分配并且标记在空闲池里的内存空间。如图一所示。
下面看看内存池分配内存的过程:
getty内存池分配内存的过程是这样的:
首先对于申请内存的线程来说是公平的,最先给等待时间最长的线程分配内存,这有效的保证了每个线程响应的时间以及消息的有序性
当一个线程过来申请内存的时候,会先判断剩余可分配的内存空间与空闲池里未使用的内存块的总和是否满足待分配的内存空间,如果不满足,则会阻塞当前线程直到内存足够分配,或者等到时间超时(等待时间可以配置)。超时以后会抛出内存不足的异常,并且释放锁,防止造成死锁的发生。内存池引入阻塞模式,很好的控制对系统内存的占用。
当拥有足够的内存空间可供分配时。会先根据需要分配的空间大小,到匹配的空闲池中获取。如果空闲池中有刚好匹配的空闲内存块,直接返回。当没有匹配的内存块时,则重新分配新的内存。当内存使用完以后,会释放到内存池中等待再次复用。
这里值得注意的一点是:
内存池中定义了4档大小不同的空闲池。分别是极小的128b、小的512b、中等的1024b、大的2048b。当有需要分配内存的时候,首先会先从这4个空闲池中匹配是否有合适的,这里可能会有一个疑问,如果分配的内存不等于空闲池中定义的大小时怎么办?这里参考了netty内存池的部分思想。比如需要分配64b的内存块,这里是这样设计的,当需要64b的内存时,实际也会返回128b的内存块,以此类推。这样设计的好处是避免内存频繁的allocate。提高了性能,只有需要大于2048的时候,才会新allocate一块与需要内存匹配的空间,为何定义这4档,一般来说,传输的数据大部分都小于128b,也就是说,大部分项目传输的数据都不会特别大。所以实际上用的最多的是128b以下的内存。定义这4档空闲池,能很好的涵盖大部分应用场景。大于2048的直接allocate。其实性能也是非常好的。
经过测试,如果分配符合空闲池的内存,一百万次allocate只需40毫秒左右,如果大于2048的,一百万次也只需1秒,性能非常好。稳定性也非常好