有没有人见过 java.nio.ByteBuffer 的实现,如果 putX() 调用超出容量,它会动态增长?
我想这样做的原因有两个:
- 我不知道我需要提前多少空间。
- 每次我用完空间时,我宁愿不做一个新的 ByteBuffer.allocate() 然后做一个批量 put() 。
有没有人见过 java.nio.ByteBuffer 的实现,如果 putX() 调用超出容量,它会动态增长?
我想这样做的原因有两个:
为了使异步 I/O 工作,您必须拥有连续的内存。在 C 中,您可以尝试重新分配数组,但在 Java 中,您必须分配新内存。您可以写入 a ByteArrayOutputStream
,然后ByteBuffer
在准备发送时将其转换为 a 。缺点是你在复制内存,高效 IO 的关键之一是减少内存复制的次数。
ByteBuffer 不能真正以这种方式工作,因为它的设计概念只是一个特定数组的视图,您也可以直接引用它。它无法尝试将该数组换成更大的数组而不会发生奇怪的事情。
您要使用的是DataOutput
. 最方便的方法是使用(预发布)Guava 库:
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.write(someBytes);
out.writeInt(someInt);
// ...
return out.toByteArray();
但是您也可以手动从 ByteArrayOutputStream 创建 DataOutputStream,并通过将虚假 IOException 链接到 AssertionErrors 中来处理它们。
看看 Mina IOBuffer https://mina.apache.org/mina-project/userguide/ch8-iobuffer/ch8-iobuffer.html这是一个替代品(它包装了 ByteBuffer)
不过,我建议你分配的比你需要的多,不要太担心。如果您分配一个缓冲区(尤其是直接缓冲区),操作系统会为其提供虚拟内存,但它仅在实际使用时才使用物理内存。虚拟内存应该很便宜。
看看 Netty 的DynamicChannelBuffer可能也值得一看。我觉得方便的是:
slice(int index, int length)
另一种选择是使用具有大缓冲区的直接内存。这会消耗虚拟内存,但只使用与您使用的一样多的物理内存(按页面,通常为 4K)
因此,如果您分配 1 MB 的缓冲区,它会消耗 1 MB 的虚拟内存,但唯一的操作系统会为实际使用的应用程序提供物理页面。
效果是您看到您的应用程序使用大量虚拟内存,但驻留内存量相对较少。
事实上,自动扩展缓冲区使用起来更加直观。如果你能负担得起重新分配的性能奢侈,你为什么不呢!?
Netty 正是ByteBuf
为您提供了这个。就像他们已经把java.nio
'sByteBuffer
和刮掉了边缘,使它更容易使用。
此外,它位于 Maven 的独立netty-buffer
包中,因此您无需包含完整的 Netty 套件即可使用。
我建议使用输入流从文件接收数据(如果您需要非阻塞,则使用单独的线程)然后将字节读入 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);
}
另一个解决方案是分配足够多的内存,填充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 可能是最好的解决方案。
Netty ByteBuf 在这方面做得很好。
向量允许持续增长
Vector<Byte> bFOO = new Vector<Byte>();
bFOO.add((字节)0x00);`
要序列化某些东西,您需要在条目中输入对象。您可以做的是将您的对象放入对象集合中,然后进行循环以获取迭代器并将它们放入字节数组中。然后,调用ByteBuffer.allocate(byte[].length)
。这就是我所做的,它对我有用。