首页 > 解决方案 > 字节数组输出流

问题描述

可能是一个愚蠢的问题,但字节数组发送的理想长度是outputstream多少?我在网上找不到有关此的任何信息。

我发现了很多将它们的数组大小设置为 2^X 或类似的示例。但这样做的目的是什么?

标签: javaarraysbyteoutputstream

解决方案


没有最佳尺寸。OutputStream 是一个抽象概念;它有一百万种实现。(不仅仅是“FileOutputStream 是一种实现”,而是“FileOutputStream,在 OpenJDK11 上,在 Windows 10 上,带有这个 servicepack,这个 CPU 和这么多系统内存,在这些情况下”)。

您看到的原因是为了缓冲效率。发送 1 个字节的问题通常基本上没什么问题,但有时,发送 1 个(或很少)字节会导致这种讨厌的情况:

  1. 你发送一个字节。
  2. 底层的输出流不是为了缓冲那个字节而设计的,它没有存储空间,所以它唯一能做的就是将它发送到实际的底层资源。假设 OutputStream 代表文件系统上的一个文件。
  3. 用于此的内核驱动程序的工作方式类似。(大多数操作系统会在内部进行缓冲,但您可以在打开文件时要求操作系统不要这样做)。
  4. 因此,现在需要将一个字节写入磁盘。但是,它是 SSD,您不能对 SSD 执行此操作,您一次只能写入整个单元*。这就是 SSD 的工作原理:您只能写入整个块的价值。它们不是大盘子上按顺序排列的位。
  5. 因此,内核读取整个单元格,更新您正在写入的一个字节,并将整个单元格写回 SSD。
  6. 例如,您的实际循环确实写入了大约 50,000 字节,因此本应读取和写入单个 SSD 的东西现在需要 50,000 次读取和 50,000 次写入,消耗您的 SSD 单元寿命并花费比所需时间长 50,000 倍的时间。

网络也会出现类似的问题(最终发送一个字节,包裹在 HTTP 标头中,包裹在 2 个 TCP/IP 数据包中,导致您.write(singleValue)和许多其他此类系统在网络上为每个字节发送约 1000 个字节。

那么为什么这些流不缓冲呢?

因为在某些情况下,您实际上并不希望他们这样做;考虑到特定的效率来编写 I/O 有很多理由。

有没有办法让我做点什么?

啊,你很幸运,有!BufferedWriter和朋友(BufferedOutputStream也存在)为您环绕底层流和缓冲区:

var file = new FileOutputStream("/some/path");
var wrapped = new BufferedOutputStream(file);
file.write(1); // this is a bad idea
wrapped.write(1); // this is fine

在这里,包装的写入不会导致任何事情发生,除了一些内存被推到周围。没有字节写入磁盘(缺点是如果有人绊倒电源线,它就会丢失)。只有在您关闭wrapped,或调用flush()包装,或向 写入足够数量的字节wrapped之后,包装最终才会真正将一大堆字节发送到底层流。如果制作字节数组很笨重,这就是你应该使用的。为什么要重新发明轮子?

但我想写入底层原始流

好吧,如果字节量小于单个 TCP/IP 数据包可以容纳的字节数,或者不幸的大小,那么您使用的字节太少(想象 TCP/IP 数据包可以精确地容纳 1000 个字节,然后发送 1001 和字节。这是一个完整的数据包,然后是第二个只有 1 个字节的数据包,效率只有 50%。50% 仍然比 0.1% 的效率要好,在这个假设中,一次一个字节可以让你得到)。但是,如果你发送,比如说,5001 字节,那就是 5 个完整的数据包和一个令人遗憾的 1 字节数据包,效率为 83.35%。不幸的是,它没有接近 100,但也没有那么糟糕。这同样适用于磁盘(如果 SSD 单元保存 2048 字节,而您发送 65537 字节,它的效率仍然约为 96/7%)。

如果对您自己的 java 进程的影响会导致这成为问题,那么您使用的字节太多:它会导致过多的垃圾收集,或者更糟糕的是,内存不足错误。

那么“甜蜜点”在哪里呢?取决于一点点,但65536很常见,不太可能“太低”。除非您同时运行数千个线程,否则它也不会太高。

它通常是 2 的幂,主要是因为迷信,但它有一些意义:那些底层的缓冲区通常是 2 的幂(毕竟计算机是二进制的东西)。因此,如果单元格大小恰好是 2048,那么如果发送 65536 个字节(这正好是 32 个单元格的数据),那么您的效率是 100%。

但是,您真正要避免的唯一事情是,如果您一次将一个字节写入打包(SSD、网络等)底层流,则会出现 0.1% 的效率。因此,没关系,只要超过2048左右,你就应该已经避免了厄运的情况。

*) 我过于简单化了;关键是单个字节的读取或写入可能需要它们中的一整块时间,并给出一些暗示为什么会这样,而不是对 SSD 技术进行完整的深入研究。


推荐阅读