跳到主要内容

字节数

概述

ActiveJ的目的是使高效而高级的I/O。 这需要大量使用用户空间的 字节缓冲区。 不幸的是,传统的Java ByteBuffer,给GC带来了沉重的负担。

为了减少GC开销,ActiveJ引入了自己的GC友好和轻量级的 ByteBufs,它可以与 ByteBufPool重复使用。

此外,常见的I/O模式是将 ByteBuffers作为一个队列。I/O操作产生数据,而应用程序消耗数据,反之亦然。 ByteBufs也是为了促进这种模式,提供专门的 ByteBufs 类,在多个 ByteBufs中进行类似队列的操作。

字节Buf

与 Java NIO ByteBuffer 相比, 一个非常轻量和效率极高的实现方法。 没有直接的缓冲器, ,这就简化和改善了 ByteBuf 性能。

ByteBuf 类似于FIFO字节队列,有两个位置: headtail。 当你向 ByteBuf写数据时,它的 尾部 ,按写的字节数增加。 同样,当你从 ByteBuf, 它的 head 读取的字节数会增加。

您只能从 Bytebuf 读取字节。其 尾部 大于 。 同样,你可以向 ByteBuf 写字节,直到 尾部 不超过包裹的数组的长度。 这样一来,就不需要 ByteBuffer.flip() 操作。

ByteBuf 支持并发进程:当一个进程向 ByteBuf写一些数据时,另一个进程可以 读取它。

要创建一个 ByteBuf ,你可以将你的字节数组包裹到 ByteBuf ,或者从 ByteBufPool中分配它。

note

如果你创建了一个 ByteBuf* 而没有从 ByteBufPool中分配它,调用 ByteBuf.recycle() 将不会有任何影响,这样的 ByteBufs会被GC简单地收集起来。

字节BufPool

ByteBufPool 允许重复使用 ByteBufs,从而减少GC负载。 为了使 ByteBufPool 使用更方便,有调试和监控工具,用于分配 ByteBuf,包括它们的堆栈痕迹。

要从池中获得一个 ByteBuf ,使用 ByteBufPool.allocate(int size)。 将分配一个四舍五入到下一个 最接近的2次方大小的 ByteBuf (例如,如果 大小 是29,将分配一个32字节的 ByteBuf)。

要将 ByteBuf 返回到 ByteBufPool,使用 ByteBuf.recycle()。 与C/C等语言相比,不需要 ,回收 ByteBufs - 在最坏的情况下,它们会被GC收集。

为了使一切保持一致,ActiveJ依赖于 "所有权 "的概念(就像Rust语言一样)--在分配之后, ,组件将一个 byteBuf ,从一个传到另一个,直到最后的 "所有者 "将其回收到 ByteBufPool

你可以在这里探索 ByteBufPool 用例

字符串

ByteBufs 类提供了对多个 ByteBuf的有效管理。 它创建了一个由几个 ByteBufs组成的具有FIFO规则的优化队列。

你可以在这里探索 ByteBufs 用例

实用类

ByteBuf模块还包含 utility classes 来管理和调整底层字节缓冲区的大小, String 转换,等等。

实例

  • ByteBuf 示例 - 表示一些基本的 ByteBuf 的可能性,例如。

    • 将数据包裹在 ByteBuf ,用于写/读。
    • 将数据中的特定部分切成片状。
    • 转换。
  • ByteBuf Pool 示例 - 表示如何使用 ByteBufPool

  • ByteBufs 示例 - 显示了如何创建和处理 ByteBuf的队列。

note

要运行例子,你需要从GitHub克隆ActiveJ

git clone https://github.com/activej/activej

并将其作为一个Maven项目导入。 查看标签 v5.0。 在运行这些例子之前,先建立项目。 这些例子位于 activej/examples/core/bytebuf

ByteBuf例子

如果你运行这个例子,你会收到以下输出。

012345
[0, 1, 2, 3, 4, 5]
你好
切片的`ByteBuf`阵列。[1, 2, 3]
从`ByteBuffer`转换的`ByteBuf`数组。[1, 2, 3]
  • 前六行是将一个字节数组包裹到一个 ByteBuf 包装器中进行读取,然后打印的结果。
byte[] data = new byte[]{0, 1, 2, 3, 4, 5};ByteBuf byteBuf = ByteBuf.wrapForReading(data);
  • 这一行 [0, 1, 2, 3, 4, 5] ,是将一个空的字节数组转换为 ByteBuf ,并将它们包装起来,以便 。 然后,在 while 循环的帮助下, ByteBuf 被填充了字节。
byte[] data = new byte[6];ByteBuf byteBuf = ByteBuf.wrapForWriting(data);byte value = 0;while (byteBuf.canWrite()) {  byteBuf.writeByte(value++);}
  • "Hello "行首先从字符串转换为 ByteBuf ,并包裹起来用于读取,然后在 byteBuf.asString()的帮助下表示为字符串用于输出。
String message = "Hello";ByteBuf byteBuf = ByteBuf.wrapForReading(message.getBytes(UTF_8));String unWrappedMessage = byteBuf.asString(UTF_8);
  • 最后两个输出代表了 ByteBuf的一些其他可能性,比如说切片。
byte[] data = new byte[]{0, 1, 2, 3, 4, 5};ByteBuf byteBuf = ByteBuf.wrap(data, 0, data.length);ByteBuf slice = byteBuf.slice(1, 3);

并将默认的 ByteBuffer 转换为 ByteBuf

ByteBuf byteBuf = ByteBuf.wrap(new byte[20], 0, 0);ByteBuffer buffer = byteBuf.toWriteByteBuffer();buffer.put((byte) 1);buffer.put((byte) 2);buffer.put((byte) 3);byteBuf.ofWriteByteBuffer(buffer);

在GitHub上看到完整的例子

ByteBuf池的例子

如果你运行这个例子,你会收到以下输出。

分配的ByteBuf阵列的长度:128回收前池中的ByteBufs的数量。0回收后池中的ByteBufs的数量。1池中的ByteBufs数量。0
ByteBuf的大小:4写完3个字节后ByteBuf的剩余字节。1一个新的ByteBuf的剩余字节数:5
[0, 1, 2, 3, 4, 5]

让我们看一下实施情况。

public final class ByteBufPoolExample {  /* Setting ByteBufPool minSize and maxSize properties here for illustrative purposes.   Otherwise, ByteBufs with size less than 32 would not be placed into pool   */  static {    System.setProperty("ByteBufPool.minSize", "1");  }
  private static void allocatingBufs() {    // Allocating a ByteBuf of 100 bytes    ByteBuf byteBuf = ByteBufPool.allocate(100);
    // Allocated ByteBuf has an array with size equal to next power of 2, hence 128    System.out.println("Length of array of allocated ByteBuf: " + byteBuf.writeRemaining());
    // Pool has 0 ByteBufs right now    System.out.println("Number of ByteBufs in pool before recycling: " + ByteBufPool.getStats().getPoolItems());
    // Recycling ByteBuf to put it back to pool    byteBuf.recycle();
    // Now pool consists of 1 ByteBuf that is the one we just recycled    System.out.println("Number of ByteBufs in pool after recycling: " + ByteBufPool.getStats().getPoolItems());
    // Trying to allocate another ByteBuf    ByteBuf anotherByteBuf = ByteBufPool.allocate(123);
    // Pool is now empty as the only ByteBuf in pool has just been taken from the pool    System.out.println("Number of ByteBufs in pool: " + ByteBufPool.getStats().getPoolItems());    System.out.println();  }
  private static void ensuringWriteRemaining() {    ByteBuf byteBuf = ByteBufPool.allocate(3);
    // Size is equal to power of 2 that is larger than 3, hence 4    System.out.println("Size of ByteBuf: " + byteBuf.writeRemaining());
    byteBuf.write(new byte[]{0, 1, 2});
    // After writing 3 bytes into ByteBuf we have only 1 spare byte in ByteBuf    System.out.println("Remaining bytes of ByteBuf after 3 bytes have been written: " + byteBuf.writeRemaining());
    // We need to write 3 more bytes, so we have to ensure that there are 3 spare bytes in ByteBuf    // and if there are not - create new ByteBuf with enough room for 3 bytes (old ByteBuf will get recycled)    ByteBuf newByteBuf = ByteBufPool.ensureWriteRemaining(byteBuf, 3);    System.out.println("Amount of ByteBufs in pool:" + ByteBufPool.getStats().getPoolItems());
    // As we need to write 3 more bytes, we need a ByteBuf that can hold 6 bytes.    // The next power of 2 is 8, so considering 3 bytes that have already been written, new ByteBuf    // can store (8-3=5) more bytes    System.out.println("Remaining bytes of a new ByteBuf: " + newByteBuf.writeRemaining());
    // Recycling a new ByteBuf (remember, the old one has already been recycled)    newByteBuf.recycle();    System.out.println();  }
  private static void appendingBufs() {    ByteBuf bufOne = ByteBuf.wrapForReading(new byte[]{0, 1, 2});    ByteBuf bufTwo = ByteBuf.wrapForReading(new byte[]{3, 4, 5});
    ByteBuf appendedBuf = ByteBufPool.append(bufOne, bufTwo);
    // Appended ByteBuf consists of two ByteBufs, you don't have to worry about allocating ByteBuf    // with enough capacity or how to properly copy bytes, ByteBufPool will handle it for you    System.out.println(Arrays.toString(appendedBuf.asArray()));    System.out.println();  }
  public static void main(String[] args) {    allocatingBufs();    ensuringWriteRemaining();    appendingBufs();  }}

在GitHub上看到完整的例子

ByteBufs示例

如果你运行这个例子,你会收到以下输出。

bufs:2 bytes:7
Buf取自bufs。[0, 1, 2, 3]
Buf取自bufs。[3, 4, 5, 6, 7, 8]
[1, 2, 3, 4][5, 6, 7, 8]'ByteBufs'是否为空? 真

第一行表示我们在添加了两个bufs之后的队列: [0, 1, 2, 3][3, 4, 5]BUFS.add() 方法。

BUFS.add(ByteBuf.wrapForReading(new byte[]{0, 1, 2, 3}));BUFS.add(ByteBuf.wrapForReading(new byte[]{3, 4, 5}));
// bufs consist of 2 Bufs at this moment

然后应用方法 BUFS.take() ,从队列中取出第一个添加的buf,即 [0, 1, 2, 3]。 下一行表示两个操作的结果:添加一个新的 [6, 7, 8] buf,然后应用 BUFS.takeRemaining() ,从队列中取出所有剩余的bufs。

// Adding one more ByteBuf to bufsBUFS.add(ByteBuf.wrapForReading(new byte[]{6, 7, 8}));
ByteBuf takenBuf = BUFS.takeRemaining();
// Taken ByteBuf is combined of every ByteBuf that were in bufs
note

注意 take()poll() ByteBufs 方法之间的区别。 当使用 take(),你必须确保队列中至少有一个 ByteBuf ,否则 使用 poll() ,它可能返回 null

最后,最后三行代表以下操作。

  • 创建两个bufs: [1, 2, 3, 4][5, 6, 7, 8]
  • 将队列中的内容排给消费者,由消费者打印出bufs。
  • 然后我们检查该队列现在是否为空。
import io.activej.bytebuf.ByteBuf;import io.activej.bytebuf.ByteBufs;
import java.util.Arrays;
public final class ByteBufsExample {  private static final ByteBufs BUFS = new ByteBufs();
  private static void addingToBufs() {    //[START REGION_1]    BUFS.add(ByteBuf.wrapForReading(new byte[]{0, 1, 2, 3}));    BUFS.add(ByteBuf.wrapForReading(new byte[]{3, 4, 5}));
    // bufs consist of 2 Bufs at this moment    //[END REGION_1]    System.out.println(BUFS);    System.out.println();  }
  private static void takingBufOutOfBufs() {    ByteBuf takenBuf = BUFS.take();
    // Buf that is taken is the one that was put in bufs first    System.out.println("Buf taken from bufs: " + Arrays.toString(takenBuf.asArray()));    System.out.println();  }
  private static void takingEverythingOutOfBufs() {    //[START REGION_2]    // Adding one more ByteBuf to bufs    BUFS.add(ByteBuf.wrapForReading(new byte[]{6, 7, 8}));
    ByteBuf takenBuf = BUFS.takeRemaining();
    // Taken ByteBuf is combined of every ByteBuf that were in bufs    //[END REGION_2]    System.out.println("Buf taken from bufs: " + Arrays.toString(takenBuf.asArray()));    System.out.println("Is 'ByteBufs' empty? " + BUFS.isEmpty());    System.out.println();  }
  public static void main(String[] args) {    addingToBufs();    takingBufOutOfBufs();    takingEverythingOutOfBufs();  }}

在GitHub上看到完整的例子