深入浅出Netty内存管理:PoolChunkList

JosMLXB 3年前
   <p>本文主要分析管理PoolChunk生命周期的PoolChunkList。</p>    <h2><strong>PoolChunkList</strong></h2>    <p>PoolChunkList负责管理多个chunk的生命周期,在此基础上对内存分配进行进一步的优化。</p>    <pre>  <code class="language-javascript">final class PoolChunkList<T> implements PoolChunkListMetric {         private final PoolChunkList<T> nextList;      private final int minUsage;      private final int maxUsage;         private PoolChunk<T> head;      private PoolChunkList<T> prevList;      ...  }  </code></pre>    <p>从代码实现可以看出,每个PoolChunkList实例维护了一个PoolChunk链表,自身也形成一个链表,为何要这么实现?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b68687c38ed80c6463d3cf59dd1a1bff.png"></p>    <p>随着chunk中page的不断分配和释放,会导致很多碎片内存段,大大增加了之后分配一段连续内存的失败率,针对这种情况,可以把内存使用率较大的chunk放到PoolChunkList链表更后面,具体实现如下:</p>    <pre>  <code class="language-javascript">boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {      if (head == null) {          return false;      }         for (PoolChunk<T> cur = head;;) {          long handle = cur.allocate(normCapacity);          if (handle < 0) {              cur = cur.next;              if (cur == null) {                  return false;              }          } else {              cur.initBuf(buf, handle, reqCapacity);              if (cur.usage() >= maxUsage) {  // (1)                  remove(cur);                  nextList.add(cur);              }              return true;          }      }  }  </code></pre>    <p>假设poolChunkList中已经存在多个chunk。当分配完内存后,如果当前chunk的使用量超过maxUsage,则把该chunk从当前链表中删除,添加到下一个链表中。</p>    <p>但是,随便chunk中内存的释放,其内存使用率也会随着下降,当下降到minUsage时,该chunk会移动到前一个列表中,实现如下:</p>    <pre>  <code class="language-javascript">boolean free(PoolChunk<T> chunk, long handle) {      chunk.free(handle);      if (chunk.usage() < minUsage) {          remove(chunk);          if (prevList == null) {              assert chunk.usage() == 0;              return false;          } else {              prevList.add(chunk);              return true;          }      }      return true;  }  </code></pre>    <p>从poolChunkList的实现可以看出,每个chunkList的都有一个上下限:minUsage和maxUsage,两个相邻的chunkList,前一个的maxUsage和后一个的minUsage必须有一段交叉值进行缓冲,否则会出现某个chunk的usage处于临界值,而导致不停的在两个chunk间移动。</p>    <p>所以chunk的生命周期不会固定在某个chunkList中,随着内存的分配和释放,根据当前的内存使用率,在chunkList链表中前后移动。</p>    <p> </p>    <p>来自:http://blog.jobbole.com/106085/</p>    <p> </p>