作者:崔海东

1. 导读

在java的世界里好像已经不再需要关注内存申请、内存回收这些直接内存操作了。更多的时候java程序员是在讨论垃圾回收器(内存分代、回收算法)等内存自动回收技术。操作系统中各种相关名词“内存池”、“内存碎片”、“malloc”、“free”似乎早已远去。

本文借助于内存池的java实现—Netty内存池,对相关理论和实现进行了阐述以使我们重新复习一下内存分配和回收的相关知识。

2. 背景

计算机世界中存在着各种各样的“池子”,如线程池、连接池、内存池。在java世界中可能接触的最多的是线程池/连接池,内存池好像比较陌生,更多的时候对内存池的印象只停留在上学时期操作系统课程中的内存管理相关内容。因此本文要讲的主要内容为内存池的一个java实现—Netty内存池。为了能较明白的理解Netty内存池的设计和实现,本文从以下几个方面对内存池进行了分析。

3. 为什么需要内存池

在做一件事情前如果不了解它存在的意义那么必将陷入“只见树木不见森林”的误区。因此弄清楚内存池存在的意义会是很有必要的。这里先不直接去说明为什么需要内存池,可以举例思考下面几个场景。

直观上感觉java中内存的分配和回收完全是由jvm所控制的,jvm对内存管理高效并且方便(不需要要求程序员时刻想着内存释放)。的确,jvm内存分配确实是高效的,但是是否所有的场景下jvm的自动的分配和回收都是高效的呢?是否还有着更好的实现方案?对于有着更高要求的情况也是这样的吗?对上面几个问题分别简单思考一下(当然这并不是要读者联想到如C++语言的手动内存分配和回收,仅仅是针对java中的一些典型情况)。

(1)是否所有的场景下jvm的自动的分配和回收都是高效的呢?

对于需要使用缓冲区Buffer的io操作来讲,情况却稍有不同。这主要体现在两个方面:频繁的内存Buffer的创建销毁这必然会对jvm的GC造成一定的压力,进而影响服务的性能,因为既然是io密集型的业务为什么不cache一份呢;如果对内存使用有着更高的要求,如使用堆外直接内存的分配和回收,虽然堆外内存的使用效率会比较高,但是其内存申请的操作会比堆内存的申请更加耗时,因此频繁的申请和释放将是一件更加耗时的操作。

(2)是否还有着更好的实现方案?

受限于当下的软件和硬件的处理速度或许一时想不到更好的方案对 这些特殊的场景进行优化,这时我们可以借助于一些计算机领域的传统理论“内存池”。进而就是java语言实现的内存池-Netty内存池。针对上面的情况可以看一下一个比较权威的官方测试结果-Twitter使 用Netty内存池和不使用内存池的性能对比(ps为什么算是比较权威官方的测试呢?因为Netty的创始人加入了twitter,并且Netty在其公司内部被广泛使用)。

https://mmbiz.qpic.cn/mmbiz_png/2VY3NksPSaEedmZpazWmZXicl7x5XdeJkDYqeBbGFEtqWwImibIZywFba0Ozusic2yQezaLLqevU0YiaI0ibAQn8KnQ/640?wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1

                                                                       图1:内存分配池化和非池化对比

                                      (出自twitter技术博客netty-4-at-twitter-reduced-gc-overhead)