netty源码解解析(4.0)-23 ByteBuf内存管理:分配和释放
bytebuf内存分配和释放由具体实现负责,抽象类型只定义的内存分配和释放的时机。
内存分配分两个阶段: 第一阶段,初始化时分配内存。第二阶段: 内存不够用时分配新的内存。bytebuf抽象层没有定义第一阶段的行为,但定义了第二阶段的方法:
public abstract bytebuf capacity(int newcapacity)
这个方法负责分配一个长度为newcapacity的新内存。
内存释放的抽象实现在abstractreferencecountedbytebuf中实现,这个类实现引用计数,当调用release方法把引用计数变成0时,会调用
protected abstract void deallocate()
执行真正的内存释放操作。
内存相关的属性
bytebuf定义了两个内存相关的属性:
capacity: 当前的当前的容量,也就是当前使用的内存大小。使用capacity()方法获得。
maxcapacity: 最大容量,也就是可以使用的最大内存大小。使用newcapacity()方法获得。
内存分配时机
abstractbytebuf定义了内存分配的时机。当writexx方法被调用的时候,如果如果发现可写空间不足,就调用capacity分配新的内存。下面以writeint为例详细分析这个过程。
1 @override 2 public bytebuf writeint(int value) { 3 ensurewritable0(4); 4 _setint(writerindex, value); 5 writerindex += 4; 6 return this; 7 } 8 9 10 final void ensurewritable0(int minwritablebytes) { 11 ensureaccessible(); 12 if (minwritablebytes <= writablebytes()) { 13 return; 14 } 15 16 if (minwritablebytes > maxcapacity - writerindex) { 17 throw new indexoutofboundsexception(string.format( 18 "writerindex(%d) + minwritablebytes(%d) exceeds maxcapacity(%d): %s", 19 writerindex, minwritablebytes, maxcapacity, this)); 20 } 21 22 // normalize the current capacity to the power of 2. 23 int newcapacity = calculatenewcapacity(writerindex + minwritablebytes); 24 25 // adjust to the new capacity. 26 capacity(newcapacity); 27 } 28
3行: ensurewritable确保当前能写入4byte的数据。
11行: 确保当前bytebuf是可以访问的。防止多线程环境下,bytebuf内存被释放后读写数据。
12,13行: 如果内存够用,就此作罢。
16行: 如果需要内存大于maxcapacity抛出异常。
23, 26行行: 计算新内存的大小, 调用capacity(int)分配新内存。
重新分配内存之前的一个重要步骤的计算新内存的大小。这个工作由calculatenewcapacity方法完成,它的代码如下:
1 private int calculatenewcapacity(int minnewcapacity) { 2 final int maxcapacity = this.maxcapacity; 3 final int threshold = 1048576 * 4; // 4 mib page 4 5 if (minnewcapacity == threshold) { 6 return threshold; 7 } 8 9 // if over threshold, do not double but just increase by threshold. 10 if (minnewcapacity > threshold) { 11 int newcapacity = minnewcapacity / threshold * threshold; 12 if (newcapacity > maxcapacity - threshold) { 13 newcapacity = maxcapacity; 14 } else { 15 newcapacity += threshold; 16 } 17 return newcapacity; 18 } 19 20 // not over threshold. double up to 4 mib, starting from 64. 21 int newcapacity = 64; 22 while (newcapacity < minnewcapacity) { 23 newcapacity <<= 1; 24 } 25 26 return math.min(newcapacity, maxcapacity); 27 }
1行:接受一个最小的新内存参数minnewcapacity。
3行: 定义一个4mb的阈值常量threshold。
5,6行: 如果minnewcapacity==threshold,那么新内存大小就是threshold。
10-17行: 如果minnewcapacity>threshold, 新内存大小是min(maxcapacity, threshold * n)且threshold * n >= minnewcapacity。
21-26行: 如果minnewcapacity<threshold, 新内存大小是min(maxcapacity, 64 * 2n)且64 * 2n >= minnewcapacity。
内存分配和释放的具体实现
本章涉及到的内存分配和释放的具体实现只涉及到unpooled类型的bytebuf,即:
unpooledheapbytebuf
unpooleddirectbytebuf
unpooledunsafeheapbytebuf
unpooledunsafedirectbytebuf
这几个具体实现中涉及到的内存分配和释放代码比较简洁,更容易明白bytebuf内存管理的原理。相比之下,pooled类型的bytebuf内存分配和释放的代码要复杂很多,会在后面的章节独立分析。
unpooledheapbytebuf和unpooledunsafeheapbytebuf实现
unpooledheapbytebuf中,内存分配的实现代码主要集中在capacity(int)和allocatearray()方法中。capacity分配新内存的步骤是
- 调用allocatearray分配一块新内存。
- 把旧内存中的实际复制到新内存中。
- 使用新内存替换旧内存(24行)。
- 释放掉旧内存(25行)。
代码如下:
1 @override 2 public bytebuf capacity(int newcapacity) { 3 checknewcapacity(newcapacity); 4 5 int oldcapacity = array.length; 6 byte[] oldarray = array; 7 if (newcapacity > oldcapacity) { 8 byte[] newarray = allocatearray(newcapacity); 9 system.arraycopy(oldarray, 0, newarray, 0, oldarray.length); 10 setarray(newarray); 11 freearray(oldarray); 12 } else if (newcapacity < oldcapacity) { 13 byte[] newarray = allocatearray(newcapacity); 14 int readerindex = readerindex(); 15 if (readerindex < newcapacity) { 16 int writerindex = writerindex(); 17 if (writerindex > newcapacity) { 18 writerindex(writerindex = newcapacity); 19 } 20 system.arraycopy(oldarray, readerindex, newarray, readerindex, writerindex - readerindex); 21 } else { 22 setindex(newcapacity, newcapacity); 23 } 24 setarray(newarray); 25 freearray(oldarray); 26 } 27 return this; 28 } 29
capacity中复制旧内存数据到新内存中的时候分两种情况(newcapacity,oldcapacity分别是新旧内存的大小):
- newcapacity>oldcapacity,这种情况比较简单,直接复制就好了(第8行)。不影响readerindex和writerindex。
- newcapacity<oldcapacity, 这种情况比较复杂。capacity尽量把可读的数据复制新内存中。
- 如果readerindex<newcapacity且writerindex<newcapacity。可读数据会全部转移到新内存中。readerindex和writerindex保持不变。
- 如果readerindex<newcapacity且writeindex>newcapacity。可端数据会部分转移的新内存中,会丢失部分可读数据。readerindex不变,writerindex变成newcapacity。
- 如果readerindex>newcapacity,数据全部丢失,readerindex和writerindex都会变成newcapacity。
allocatearray方法负责分配一块新内存,它的实现是new byte[]。freearray方法负责释放内存,这个方法是个空方法。
unpooledunsafeheapbytebuf是unloopedheadpbytebuf的直接子类,在内存管理上的差别是allocatearray的实现,unpooledunsafeheapbytebuf的实现是:
1 @override 2 byte[] allocatearray(int initialcapacity) { 3 return platformdependent.allocateuninitializedarray(initialcapacity); 4 }
unpooleddirectbytebuf和unpooledunsafedirectbytebuf实现
unpooleddirectbytebuf类使用directbytebuffer作为内存,使用了directbytebuffer的能力来实现bytebuf接口。allocatedirect和freedirect方法负责分配和释放directbytebuffer。capacity(int)方法和unloopedheapbytebuf类似,使用allocatedirect创建一个新的directbytebuffer, 把旧内存数据复制到新内存中,然后使用新内存替换旧内存,最后调用freedirect方法释放掉旧的directbytebuffer。
1 protected bytebuffer allocatedirect(int initialcapacity) { 2 return bytebuffer.allocatedirect(initialcapacity); 3 } 4 5 protected void freedirect(bytebuffer buffer) { 6 platformdependent.freedirectbuffer(buffer); 7 } 8 9 @override 10 public bytebuf capacity(int newcapacity) { 11 checknewcapacity(newcapacity); 12 13 int readerindex = readerindex(); 14 int writerindex = writerindex(); 15 16 int oldcapacity = capacity; 17 if (newcapacity > oldcapacity) { 18 bytebuffer oldbuffer = buffer; 19 bytebuffer newbuffer = allocatedirect(newcapacity); 20 oldbuffer.position(0).limit(oldbuffer.capacity()); 21 newbuffer.position(0).limit(oldbuffer.capacity()); 22 newbuffer.put(oldbuffer); 23 newbuffer.clear(); 24 setbytebuffer(newbuffer); 25 } else if (newcapacity < oldcapacity) { 26 bytebuffer oldbuffer = buffer; 27 bytebuffer newbuffer = allocatedirect(newcapacity); 28 if (readerindex < newcapacity) { 29 if (writerindex > newcapacity) { 30 writerindex(writerindex = newcapacity); 31 } 32 oldbuffer.position(readerindex).limit(writerindex); 33 newbuffer.position(readerindex).limit(writerindex); 34 newbuffer.put(oldbuffer); 35 newbuffer.clear(); 36 } else { 37 setindex(newcapacity, newcapacity); 38 } 39 setbytebuffer(newbuffer); 40 } 41 return this; 42 }
对比unloopedheapbytebuf的capacity(int)方法,发现这两个实现非常类似,也分两种情况处理:
- 18-24行,newcapacity > oldcapacity的情况。
- 26-39行, newcapacity < oldcapacity的情况。
两种情况对readerindex和writerindex的影响也一样,不同的是数据复制时的具体实现。
unpooledunsafedirectbytebuf和unpooleddirectbytebuf同属于abstractreferencecountedbytebuf的派生类,它们之间没有继承关系。但内存分配和释放实现是一样的,不同的地方是内存i/o。unpooledunsafedirectbytebuf使用unsafebytebufutil类之间读写directbytebuffer的内存,没有使用directbytebuffer的i/o能力。
下一篇: 囧事连连,笑倒一片