netty UnpooledHeapByteBuf 源码分析

时间:2022-11-06 21:55:39

UnpooledHeapByteBuf 是基于堆内存进行内存分配的字节缓冲区,没有基于对象池技术实现,这意味着每次I/O的读写都会创建一个新的UnpooledHeapByteBuf,频繁进行大块内存的分配和回收对性能会造成一定的影响,但是对比与堆外内存的申请和释放,它的成本会低一些。

相对与PooledHeapByteBuf,UnpooledHeapByteBuf 的实现原理更加简单,也不容易出现内存管理方面的问题,在满足性能的情况下,尽量使用UnpooledHeapByteBuf 。

1.成员变量

private final ByteBufAllocator alloc;//聚合一个ByteBufAllocator,用于UnpooledHeapByteBuf的内存分配
private byte[] array;//byte 数组作为缓冲区
private ByteBuffer tmpNioBuf;//用于实现Netty ByteBuf 到 JDK ByteBuffer 的转换

事实上,如果使用JDK 的ByteBuffer替换byte数组也是可行的 ,直接使用byte数组的根本原因是提升性能和更加便捷的进行位操作。JDK 的ByteBuffer底层实现也是byte数组,如下。

public abstract class ByteBuffer
extends Buffer
implements Comparable<ByteBuffer>{ // These fields are declared here rather than in Heap-X-Buffer in order to
// reduce the number of virtual method invocations needed to access these
// values, which is especially costly when coding small buffers.
//
final byte[] hb; // Non-null only for heap buffers
final int offset;
boolean isReadOnly; // Valid only for heap buffers

2.动态扩展缓冲区

@Override
public ByteBuf capacity(int newCapacity) {
ensureAccessible();
if (newCapacity < 0 || newCapacity > maxCapacity()) {
throw new IllegalArgumentException("newCapacity: " + newCapacity);
} int oldCapacity = array.length;
if (newCapacity > oldCapacity) {
byte[] newArray = new byte[newCapacity];
System.arraycopy(array, 0, newArray, 0, array.length);
setArray(newArray);
} else if (newCapacity < oldCapacity) {
byte[] newArray = new byte[newCapacity];
int readerIndex = readerIndex();
if (readerIndex < newCapacity) {
int writerIndex = writerIndex();
if (writerIndex > newCapacity) {
writerIndex(writerIndex = newCapacity);
}
System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
} else {
setIndex(newCapacity, newCapacity);
}
setArray(newArray);
}
return this;
}
/**
* Should be called by every method that tries to access the buffers content to check
* if the buffer was released before.
*/
protected final void ensureAccessible() {
if (refCnt() == 0) {
throw new IllegalReferenceCountException(0);
}
}

方法 的入口首先对新容量进行合法性校验,如果大于容量上限或者小于0,则抛出IllegalArgumentException异常。

判断新的容量值是否大于当前的缓冲区容量,如果大于则需要动态扩展,通过 byte[] newArray = new byte[newCapacity];创建新的缓冲区字节数组,然后通过 System.arraycopy进行内存复制,将旧的字节数组复制到新创建的字节数组中,最后调用setArray(newArray);替换旧的字节数组。

private void setArray(byte[] initialArray) {
array = initialArray;
tmpNioBuf = null;
}

动态扩容完成后,需要将原来的视图tmpNioBuf设置为空。

如果新的容量小于当前缓冲区容量不需要动态扩展,但是需要截取当前缓冲区创建一个新的子缓冲区。先判断下读索引是否小于新的容量值,如果小于进一步判断写索引是否大于新的容量值,如果大于则将写索引设置为新的容量值(防止越界)。更新完写索引之后,通过 System.arraycopy将当前可读的字节数组复制到新创建的子缓冲区中, System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);

如果新的容量值小于读索引,说明没有可读的字节数组需要复制到新创建的缓冲区中,将读写索引为新的容量值即可。最后调用setArray方法替换原来的字节数组。

3.字节数组复制

@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
checkSrcIndex(index, length, srcIndex, src.length);
System.arraycopy(src, srcIndex, array, index, length);
return this;
}

首先做合法性校验。

protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
checkIndex(index, length);
if (srcIndex < 0 || srcIndex > srcCapacity - length) {
throw new IndexOutOfBoundsException(String.format(
"srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
}
}

校验index,length的值,如果小于0,抛出IllegalArgumentException异常,然后对两者之和进行判断,如果大于缓冲区的容量,则抛出IndexOutOfBoundsException异常。srcIndex和srcCapacity,与之类似。校验通过之后,调用 System.arraycopy(src, srcIndex, array, index, length)方法进行字节数组的复制。

protected final void checkIndex(int index, int fieldLength) {
ensureAccessible();
if (fieldLength < 0) {
throw new IllegalArgumentException("length: " + fieldLength + " (expected: >= 0)");
}
if (index < 0 || index > capacity() - fieldLength) {
throw new IndexOutOfBoundsException(String.format(
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity()));
}
}

注意: ButeBuf以set和get开头读写缓冲区的方法不会修改读写索引。

4.转换为JDK ByteBuffer

ByteBuffer 是基于byte数组实现,NIO的ByteBuffer提供wrap方法,可以将byte数组转换成ByteBuffer对象。

/**
* Wraps a byte array into a buffer.
*
* <p> The new buffer will be backed by the given byte array;
* that is, modifications to the buffer will cause the array to be modified
* and vice versa. The new buffer's capacity will be
* <tt>array.length</tt>, its position will be <tt>offset</tt>, its limit
* will be <tt>offset + length</tt>, and its mark will be undefined. Its
* {@link #array backing array} will be the given array, and
* its {@link #arrayOffset array offset} will be zero. </p>
*
* @param array
* The array that will back the new buffer
*
* @param offset
* The offset of the subarray to be used; must be non-negative and
* no larger than <tt>array.length</tt>. The new buffer's position
* will be set to this value.
*
* @param length
* The length of the subarray to be used;
* must be non-negative and no larger than
* <tt>array.length - offset</tt>.
* The new buffer's limit will be set to <tt>offset + length</tt>.
*
* @return The new byte buffer
*
* @throws IndexOutOfBoundsException
* If the preconditions on the <tt>offset</tt> and <tt>length</tt>
* parameters do not hold
*/
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}

UnpooledHeapByteBuf.java

@Override
public ByteBuffer nioBuffer(int index, int length) {
ensureAccessible();
return ByteBuffer.wrap(array, index, length).slice();
}

调用了ByteBuffer的slice方法,由于每次调用nioBuffer都会创建一个新的ByteBuffer,因此此处的slice方法起不到重用缓冲区的效果,只能保证读写索引的独立性。

5.与子类相关的方法

isDirect方法:如果基础堆内存实现的ByteBuf,返回false,

@Override
public boolean isDirect() {
return false;
}

hasArray方法:因为UnpooledHeapByteBuf是基于字节数组实现,返回true

@Override
public boolean hasArray() {
return true;
}

array方法:因为UnpooledHeapByteBuf是基于字节数组实现,所以返回值是内部的字节数组成员变量。调用array方法之前,可以先使用hasArray方法进行判断,如果返回false说明当前ByteBuf不支持array方法。

@Override
public byte[] array() {
ensureAccessible();
return array;
}

netty UnpooledHeapByteBuf 源码分析的更多相关文章

  1. netty &colon; NioEventLoopGroup 源码分析

    NioEventLoopGroup 源码分析 1. 在阅读源码时做了一定的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限.为了方便 IDE 查看.跟踪.调试 代码,所以在 github ...

  2. Netty ByteBuf源码分析

    Netty的ByteBuf是JDK中ByteBuffer的升级版,提供了NIO buffer和byte数组的抽象视图. ByteBuf的主要类集成关系: (图片来自Netty权威指南,图中有一个画错的 ...

  3. 【Netty】源码分析目录

    前言 为方便系统的学习Netty,特整理文章目录如下. [Netty]第一个Netty应用 [Netty]Netty核心组件介绍 [Netty]Netty传输 [Netty]Netty之ByteBuf ...

  4. NIO-EPollSelectorIpml源码分析

    目录 NIO-EPollSelectorIpml源码分析 目录 前言 初始化EPollSelectorProvider 创建EPollSelectorImpl EPollSelectorImpl结构 ...

  5. Netty源码分析第5章&lpar;ByteBuf&rpar;----&gt&semi;第2节&colon; ByteBuf的分类

    Netty源码分析第五章: ByteBuf 第二节: ByteBuf的分类 上一小节简单介绍了AbstractByteBuf这个抽象类, 这一小节对其子类的分类做一个简单的介绍 ByteBuf根据不同 ...

  6. Netty源码分析第5章&lpar;ByteBuf&rpar;----&gt&semi;第3节&colon; 缓冲区分配器

    Netty源码分析第五章: ByteBuf 第三节: 缓冲区分配器 缓冲区分配器, 顾明思议就是分配缓冲区的工具, 在netty中, 缓冲区分配器的*抽象是接口ByteBufAllocator, 里 ...

  7. Netty源码分析 (七)----- read过程 源码分析

    在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...

  8. Netty学习篇⑥--ByteBuf源码分析

    什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...

  9. netty&lpar;六&rpar; buffer 源码分析

    问题 : netty的 ByteBuff 和传统的ByteBuff的区别是什么? HeapByteBuf 和 DirectByteBuf 的区别 ? HeapByteBuf : 使用堆内存,缺点 ,s ...

随机推荐

  1. 基于内容产品的MVP探索

    王凯:凯叔讲故事的创始人 1.精益创业有以下3个过程: ——假设:价值假设和增长假设 ——认知:验证认知 ——行动:MVP(最小化可行产品) 2.互联网时代内容该如何定价: ——对于一家初创公司,最重 ...

  2. C&num;中控件数组的讨论

    VB用得习惯后,到C#中来觉得很奇怪,如此好的控件数组怎么不见了.“众所周知,控件数组最主要的两个优点:可以循环附值:可以响应同一个事件.从而大大简化了代码.引自http://wenku.baidu. ...

  3. 【转】get a mysterious problem&comma;when i use HttpWebRequest in unity c&num; script

    in script,i use HttpWebRequest to get service from network.but it comes a mysterious problem. the so ...

  4. sublime比较好用的插件

    emmet, markdown preview, package Control, SFTP, Anaconda

  5. Perl读取标准输入&lt&semi;STDIN&gt&semi;、读取文件输入&lt&semi;&gt&semi;和chomp函数

    读取标准输入<STDIN> <STDIN>表示从标准输入中读取内容,如果没有,则等待输入.<STDIN>读取到的结果中,如果没有意外,都会自带换行符. 例如,tes ...

  6. DBMS客户端是否安装:Make sure DBMS client is installed and this required library is available for dynamic loading

    Symptom The full error message is as follows:Error logging in.  Unable to process the database trans ...

  7. 机器学习技法笔记:03 Kernel Support Vector Machine

    Roadmap Kernel Trick Polynomial Kernel Gaussian Kernel Comparison of Kernels Summary

  8. javascript 点击按钮实现隐藏显示切换效果

    原文链接:http://www.jb51.net/article/79083.htm <html> <head> <meta charset="gb2312&q ...

  9. ArcGIS API for Javascript之专题图的制作(四)热力图渲染(上)

    一 .热力图定义 热力图(heat map)也称热图,是以特殊颜色高亮区域的形式表示密度.温度.气压.频率等分布的不易理解和表达的数据. 二.HeatmapRenderer esri/renderer ...

  10. MFC学习笔记(一)&colon; 不用MFC向导如何新建一个MFC程序

    使用Visual Studio新建一个空项目,项目命名为HelloMFC,完成后,打开项目属性页面,将配置属性选项卡中的常规项打开,将其中的MFC的使用属性栏改为:在静态库中使用MFC或者在共享DLL ...