【发布时间】:2020-11-24 10:45:30
【问题描述】:
可能是一个愚蠢的问题,但是通过outputstream 发送的字节数组的理想长度是多少?我在网上找不到任何关于此的内容。
我发现了很多将它们的数组大小设置为 2^X 或类似值的示例。但是这样做的目的是什么?
【问题讨论】:
标签: java arrays byte outputstream
可能是一个愚蠢的问题,但是通过outputstream 发送的字节数组的理想长度是多少?我在网上找不到任何关于此的内容。
我发现了很多将它们的数组大小设置为 2^X 或类似值的示例。但是这样做的目的是什么?
【问题讨论】:
标签: java arrays byte outputstream
没有最佳尺寸。 OutputStream 是一个抽象概念;它有一百万种实现。 (不仅仅是“FileOutputStream 是一种实现”,而是“FileOutputStream,在 OpenJDK11 上,在 Windows 10 上,带有这个 servicepack,这个 CPU 和这么多系统内存,在这些情况下”)。
您看到的原因是为了缓冲效率。发送 1 个字节的问题通常基本上没什么问题,但有时发送 1 个(或很少)字节会导致这种讨厌的情况:
网络也会出现类似的问题(最终发送一个字节,包裹在 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 技术进行完整的深入研究。
【讨论】: