49

有没有人见过 java.nio.ByteBuffer 的实现,如果 putX() 调用超出容量,它会动态增长?

我想这样做的原因有两个:

  1. 我不知道我需要提前多少空间。
  2. 每次我用完空间时,我宁愿不做一个新的 ByteBuffer.allocate() 然后做一个批量 put() 。
4

11 回答 11

43

为了使异步 I/O 工作,您必须拥有连续的内存。在 C 中,您可以尝试重新分配数组,但在 Java 中,您必须分配新内存。您可以写入 a ByteArrayOutputStream,然后ByteBuffer在准备发送时将其转换为 a 。缺点是你在复制内存,高效 IO 的关键之一是减少内存复制的次数。

于 2009-11-21T06:01:58.440 回答
10

ByteBuffer 不能真正以这种方式工作,因为它的设计概念只是一个特定数组的视图,您也可以直接引用它。它无法尝试将该数组换成更大的数组而不会发生奇怪的事情。

您要使用的是DataOutput. 最方便的方法是使用(预发布)Guava 库:

ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.write(someBytes);
out.writeInt(someInt);
// ...
return out.toByteArray();

但是您也可以手动从 ByteArrayOutputStream 创建 DataOutputStream,并通过将虚假 IOException 链接到 AssertionErrors 中来处理它们。

于 2009-11-21T16:52:00.417 回答
6

看看 Mina IOBuffer https://mina.apache.org/mina-project/userguide/ch8-iobuffer/ch8-iobuffer.html这是一个替代品(它包装了 ByteBuffer)

不过,我建议你分配的比你需要的多,不要太担心。如果您分配一个缓冲区(尤其是直接缓冲区),操作系统会为其提供虚拟内存,但它仅在实际使用时才使用物理内存。虚拟内存应该很便宜。

于 2009-11-21T08:25:17.527 回答
5

看看 Netty 的DynamicChannelBuffer可能也值得一看。我觉得方便的是:

  • slice(int index, int length)
  • 无符号操作
  • 分离的作家和读者索引
于 2010-03-04T09:42:51.997 回答
5

另一种选择是使用具有大缓冲区的直接内存。这会消耗虚拟内存,但只使用与您使用的一样多的物理内存(按页面,通常为 4K)

因此,如果您分配 1 MB 的缓冲区,它会消耗 1 MB 的虚拟内存,但唯一的操作系统会为实际使用的应用程序提供物理页面。

效果是您看到您的应用程序使用大量虚拟内存,但驻留内存量相对较少。

于 2010-03-07T09:19:13.013 回答
3

事实上,自动扩展缓冲区使用起来更加直观。如果你能负担得起重新分配的性能奢侈,你为什么不呢!?

Netty 正是ByteBuf为您提供了这个。就像他们已经把java.nio'sByteBuffer和刮掉了边缘,使它更容易使用。

此外,它位于 Maven 的独立netty-buffer包中,因此您无需包含完整的 Netty 套件即可使用。

于 2018-04-20T02:43:06.250 回答
1

我建议使用输入流从文件接收数据(如果您需要非阻塞,则使用单独的线程)然后将字节读入 ByteArrayOutstream,这使您能够将其作为字节数组获取。这是一个简单的示例,无需添加太多解决方法。

    try (InputStream inputStream = Files.newInputStream(
            Paths.get("filepath"), StandardOpenOption.READ)){

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int byteRead = 0;

        while(byteRead != -1){
            byteRead = inputStream.read();
            baos.write(byteRead);
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(baos.size())
        byteBuffer.put(baos.toByteArray());

        //. . . . use the buffer however you want

    }catch(InvalidPathException pathException){
        System.out.println("Path exception: " + pathException);
    }
    catch (IOException exception){
        System.out.println("I/O exception: " + exception); 
    }
于 2018-08-19T16:43:12.477 回答
0

另一个解决方案是分配足够多的内存,填充ByteBuffer然后只返回占用的字节数组:

初始化一个大的ByteBuffer

ByteBuffer byteBuffer = ByteBuffer.allocate(1000);

把东西放进去之后:

private static byte[] getOccupiedArray(ByteBuffer byteBuffer)
{
    int position = byteBuffer.position();
    return Arrays.copyOfRange(byteBuffer.array(), 0, position);
}

但是,org.apache.commons.io.output.ByteArrayOutputStream从一开始就使用 a 可能是最好的解决方案。

于 2020-03-21T14:44:32.227 回答
0

Netty ByteBuf 在这方面做得很好。

于 2020-12-09T22:52:54.517 回答
-5

向量允许持续增长

Vector<Byte> bFOO = new Vector<Byte>(); bFOO.add((字节)0x00);`

于 2014-04-11T10:22:05.030 回答
-6

要序列化某些东西,您需要在条目中输入对象。您可以做的是将您的对象放入对象集合中,然后进行循环以获取迭代器并将它们放入字节数组中。然后,调用ByteBuffer.allocate(byte[].length)。这就是我所做的,它对我有用。

于 2014-12-13T23:10:27.790 回答