分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 网页技术

netty UnpooledHeapByteBuf 源码分析

发布时间:2023-09-06 02:13责任编辑:彭小芳关键词:暂无标签

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.动态扩展缓冲区

@Overridepublic 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.字节数组复制

@Overridepublic 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

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

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

5.与子类相关的方法

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

@Overridepublic boolean isDirect() { ???return false;}

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

@Overridepublic boolean hasArray() { ???return true;}

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

@Overridepublic byte[] array() { ???ensureAccessible(); ???return array;}

netty UnpooledHeapByteBuf 源码分析

原文地址:https://www.cnblogs.com/zwb1234/p/9577757.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved