Netty(九)——ByteBuf源码之析

淩亂°似流年 2022-04-17 02:15 322阅读 0赞
  1. ByteBufNetty占据着中重要的位置,上篇《[Netty——ByteBuf功能之说][Netty_ByteBuf]》讲了ByteBuf的工作原理和重要功能介绍。这篇从源码的角度来看ByteBuf。首先,来看一下主要的类继承结构图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWppYWhhbjYyOTYyOQ_size_16_color_FFFFFF_t_70

  1. 我们从两个角度看下上图:
  2. 从内容分配角度上分为:1,堆内存(HeapByteBuf)字节缓冲区,特点是内存的分配和回收速度快,可被JVM自动回收;缺点是如果进行SocketI/O读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内核Channel中,性能会有一定程度的下降。2,直接内存(DirectByteBuf)字节缓冲区:在堆外进行内存分配,相比于堆内存分配和回收速度会慢一些,但是将它写入或者从Socket Channel中读取会很快。经验表明:ByteBuf的最佳实践是在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息处理使用HeapByteBuf
  3. 从内存回收角度看分为:1,对象池的ByteBuf和普通的ByteBuf。主要区别就是对象池的ByteBuf维护了一个内存池,可以循环利用创建的ByteBuf,提高内存的使用率,降低由于高负载导致的频繁GC
  4. 好,下边,我来看下各个类的源码分析,由于我这里用思维导图进行的源码分析总结,将涉及到源码的部分放到了注释中,就不一一展示了,这里先看下总结的导图吧:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWppYWhhbjYyOTYyOQ_size_16_color_FFFFFF_t_70 1

  1. 这里看下第一个吧,其它可以自己跟着思维导图查看,或者下载思维导图——[ByteBuf源码之析][ByteBuf]。AbstractByteBuf继承自ByteBuf,一些公共属性和功能会体现这个类中,源码分析:
  2. 一,主要成员变量:
  3. 说明:1,读索引、写索引、mark、最大容量等公共属性;
  4. 2leakDetector static,用于检测对象的是否泄漏;
  5. 3,将缓存区实现放到子类实现,让子类决定是基于堆内存还是直接内存。
  6. static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);
  7. int readerIndex;
  8. int writerIndex;
  9. private int markedReaderIndex;
  10. private int markedWriterIndex;
  11. private int maxCapacity;
  12. /**************************************************/
  13. 二,读操作簇:以readBytes(ByteBuf dst, int dstIndex, int length) 为例
  14. 1,方法:
  15. @Override
  16. public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) {
  17. checkReadableBytes(length);
  18. getBytes(readerIndex, dst, dstIndex, length);
  19. readerIndex += length;
  20. return this;
  21. }
  22. 2,对缓冲区可用空间的校验:
  23. /**
  24. * Throws an {@link IndexOutOfBoundsException} if the current
  25. * {@linkplain #readableBytes() readable bytes} of this buffer is less
  26. * than the specified value.
  27. */
  28. protected final void checkReadableBytes(int minimumReadableBytes) {
  29. ensureAccessible();
  30. if (minimumReadableBytes < 0) {
  31. throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)");
  32. }
  33. if (readerIndex > writerIndex - minimumReadableBytes) {
  34. throw new IndexOutOfBoundsException(String.format(
  35. "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
  36. readerIndex, minimumReadableBytes, writerIndex, this));
  37. }
  38. }
  39. 3,校验通过后,调用getBytes方法,从当前读索引开始,复制length个字节到目标byte数组中。由子类实现,不同子类实现细节不同。
  40. 4,读索引增加
  41. /**************************************************/
  42. 三,写操作簇:以writeBytes(ByteBuf src, int srcIndex, int length)为例
  43. 1,方法:
  44. @Override
  45. public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
  46. ensureAccessible();
  47. ensureWritable(length);
  48. setBytes(writerIndex, src, srcIndex, length);
  49. writerIndex += length;
  50. return this;
  51. }
  52. 1.1,写入字节数组的长度校验:
  53. @Override
  54. public ByteBuf ensureWritable(int minWritableBytes) {
  55. if (minWritableBytes < 0) {
  56. throw new IllegalArgumentException(String.format(
  57. "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
  58. }
  59. if (minWritableBytes <= writableBytes()) {
  60. return this;
  61. }
  62. if (minWritableBytes > maxCapacity - writerIndex) {
  63. throw new IndexOutOfBoundsException(String.format(
  64. "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
  65. writerIndex, minWritableBytes, maxCapacity, this));
  66. }
  67. // Normalize the current capacity to the power of 2.
  68. int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
  69. // Adjust to the new capacity.
  70. capacity(newCapacity);
  71. return this;
  72. }
  73. 1.1.1,如果当前写入数组长度大于可写字节数,则通过自身的动态扩展进行满足写需求。扩容方式:首先设置门限阈值threshold 4MB,如果=threshold直接返回,如果<threshold则以64进行倍增;如果>threshold则每次采用4MB扩张。
  74. @Override
  75. public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
  76. if (minNewCapacity < 0) {
  77. throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expectd: 0+)");
  78. }
  79. if (minNewCapacity > maxCapacity) {
  80. throw new IllegalArgumentException(String.format(
  81. "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
  82. minNewCapacity, maxCapacity));
  83. }
  84. final int threshold = 1048576 * 4; // 4 MiB page
  85. if (minNewCapacity == threshold) {
  86. return threshold;
  87. }
  88. // If over threshold, do not double but just increase by threshold.
  89. if (minNewCapacity > threshold) {
  90. int newCapacity = minNewCapacity / threshold * threshold;
  91. if (newCapacity > maxCapacity - threshold) {
  92. newCapacity = maxCapacity;
  93. } else {
  94. newCapacity += threshold;
  95. }
  96. return newCapacity;
  97. }
  98. // Not over threshold. Double up to 4 MiB, starting from 64.
  99. int newCapacity = 64;
  100. while (newCapacity < minNewCapacity) {
  101. newCapacity <<= 1;
  102. }
  103. return Math.min(newCapacity, maxCapacity);
  104. }
  105. /**************************************************/
  106. 四,操作索引:
  107. 1,与索引相关主要涉及读写索引、markrest等,比较简单,看设置读索引方法:
  108. @Override
  109. public ByteBuf readerIndex(int readerIndex) {
  110. if (readerIndex < 0 || readerIndex > writerIndex) {
  111. throw new IndexOutOfBoundsException(String.format(
  112. "readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
  113. }
  114. this.readerIndex = readerIndex;
  115. return this;
  116. }
  117. /**************************************************/
  118. 五,重用缓冲区:discardReadBytes
  119. 1,discardReadBytes方法:
  120. @Override
  121. public ByteBuf discardReadBytes() {
  122. ensureAccessible();
  123. if (readerIndex == 0) {
  124. return this;
  125. }
  126. if (readerIndex != writerIndex) {
  127. setBytes(0, this, readerIndex, writerIndex - readerIndex);
  128. writerIndex -= readerIndex;
  129. adjustMarkers(readerIndex);
  130. readerIndex = 0;
  131. } else {
  132. adjustMarkers(readerIndex);
  133. writerIndex = readerIndex = 0;
  134. }
  135. return this;
  136. }
  137. 1.1adjustMarkers(readerIndex)方法设置markedReaderIndexmarkedWriteIndex:
  138. protected final void adjustMarkers(int decrement) {
  139. int markedReaderIndex = this.markedReaderIndex;
  140. if (markedReaderIndex <= decrement) {
  141. this.markedReaderIndex = 0;
  142. int markedWriterIndex = this.markedWriterIndex;
  143. if (markedWriterIndex <= decrement) {
  144. this.markedWriterIndex = 0;
  145. } else {
  146. this.markedWriterIndex = markedWriterIndex - decrement;
  147. }
  148. } else {
  149. this.markedReaderIndex = markedReaderIndex - decrement;
  150. markedWriterIndex -= decrement;
  151. }
  152. }
  153. /**************************************************/
  154. 六,skipBytes,丢弃或者跳跃不需要字节
  155. 1,看方法源码:
  156. @Override
  157. public ByteBuf skipBytes(int length) {
  158. checkReadableBytes(length);
  159. readerIndex += length;
  160. return this;
  161. }
  162. 下边我们在来简单看下ButyBuf的几个重要的辅助类,其实都非常简单,为了更容易扩展,为了提供更多的基本功能,都是非常实用的,我们来看下简单说明:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWppYWhhbjYyOTYyOQ_size_16_color_FFFFFF_t_70 2

  1. 好,这样我么NettyByteBuf相关内容就学习完了,需要思考的是,作者对此的设计思路,实现方式,编码风格等,剩下就是我们多用多做多思考,来更进一步的理解了。接下来,Netty其它重要类源码的学习思考。。。欢迎大家多交流。
  2. PS:思维导图下载地址:[ByteBuf源码分析][ByteBuf]。也可以到我的资源中进行下载。

发表评论

表情:
评论列表 (有 0 条评论,322人围观)

还没有评论,来说两句吧...

相关阅读

    相关 netty阅读ByteBuf

    今天我们开启新的篇章,netty很重要的内存概念将在这一章介绍。ByteBuf主要介绍以下几点: 1、内存与内存管理器的抽象 2、不同规格大小和不同类别的内存的分配策略