首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Netty精粹之轻量级内存池技术实现原理与应用(4)

Netty精粹之轻量级内存池技术实现原理与应用(4)

简单的测试数据说话
下面我们来看下基于轻量级内存池和原始使用方式带来的性能数据对比,这里拿Netty提供的一个简单的可以回收的RecyclableArrayList来和传统的ArrayList来做比较,由于RecyclableArrayList和传统的ArrayList优势主要在于当频繁重复创建ArrayList对象的时候RecyclableArrayList不会真的新创建,而是会从池中获取对象来使用,而ArrayList的每次new操作都会在JVM的对内存中真枪实弹的创建一个对象,因此我们可以想象对于ArrayList的使用,青年代的内存回收相对会比较频繁,为了简单期间,我们这个例子不涉及直接内存技术,因此我们关心的地方主要是GC频率回收的改善,看看我的两段测试代码:
代码1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<code class="hljs gradle"><span class="hljs-keyword">public <span class="hljs-keyword">static <span class="hljs-keyword">void main(String ...s) {
    <span class="hljs-keyword">int i=<span class="hljs-number">0, <span class="hljs-keyword">times = <span class="hljs-number">1000000;
    <span class="hljs-keyword">byte[] data = <span class="hljs-keyword">new <span class="hljs-keyword">byte[<span class="hljs-number">1024];
    <span class="hljs-keyword">while (i++ < <span class="hljs-keyword">times) {
        RecyclableArrayList list = RecyclableArrayList.newInstance();
        <span class="hljs-keyword">int <span class="hljs-keyword">count = <span class="hljs-number">100;
        <span class="hljs-keyword">for (<span class="hljs-keyword">int j=<span class="hljs-number">0;j<<span class="hljs-keyword">count;j++){
            list.add(data);
        }
        list.recycle();
        System.out.<span class="hljs-keyword">println(<span class="hljs-string">"count:[" + <span class="hljs-keyword">count +
                <span class="hljs-string">"]");
        sleep(<span class="hljs-number">1);
    }
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>



代码2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code class="hljs gradle"><span class="hljs-keyword">public <span class="hljs-keyword">static <span class="hljs-keyword">void main(String ...s) {
    <span class="hljs-keyword">int i=<span class="hljs-number">0, <span class="hljs-keyword">times = <span class="hljs-number">1000000;
    <span class="hljs-keyword">byte[] data = <span class="hljs-keyword">new <span class="hljs-keyword">byte[<span class="hljs-number">1024];
    <span class="hljs-keyword">while (i++ < <span class="hljs-keyword">times) {
        ArrayList list = <span class="hljs-keyword">new ArrayList();
        <span class="hljs-keyword">int <span class="hljs-keyword">count = <span class="hljs-number">100;
        <span class="hljs-keyword">for (<span class="hljs-keyword">int j=<span class="hljs-number">0;j<<span class="hljs-keyword">count;j++){
            list.add(data);
        }
        System.out.<span class="hljs-keyword">println(<span class="hljs-string">"count:[" + <span class="hljs-keyword">count +
                <span class="hljs-string">"]");
        sleep(<span class="hljs-number">1);
    }
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>



上面代码逻辑相同,分别循环100w次,每次循环创建一个ArrayList对象,放入100个指向1kb大小的字节数组的引用,消耗内存的地方主要是ArrayList对象的创建,因为ArrayList的内部是对象数组实现的,因此内存消耗比较少,我们只能通过快速的循环创建来达到内存渐变的效果。

上面左图是使用传统的ArrayList测试数据,右图是使用RecyclableArrayList的测试数据,对于不可循环使用的ArrayList,GC频率相比使用RecyclableArrayList的GC频率高很多,上面的工具也给出了左图16次GC花费的时间为77.624ms而右图的3次GC花费的时间为26.740ms。

Recycler对象池总结
在Netty中,所有的IO操作基本上都要涉及缓冲区的使用,无论是上文说的HeapBuffer还是DirectBuffer,如果对于这些缓冲区不能够重复利用,后果是可想而知的。对于堆内存则会引发相对频繁的GC,而对于直接内存则会引发频繁的缓冲区创建与回收,这些操作对于两种缓冲区分别带来严重的性能损耗,Netty基于ThreadLocal实现的轻量级对象池实现在一定程度上减少了由于GC和分配回收带来的性能损耗,使得Netty线程运行的更快,总体性能更优。
总体上基于内存池技术的缓冲区实现,优点可以总结如下:
  • 对于PooledHeapBuffer的使用,Netty可以重复使用堆内存区域,降低的内存申请的频率同时也降低了JVM GC的频率。
  • 对于PooledDirectBuffer而言,Netty可以重复使用直接内存区域分配的缓冲区,这使得对于直接内存的使用在原有相比HeapBuffer的优点之外又弥补了自身分配与回收代价相对比较大的缺点。
返回列表