为什么 FileOutputStream 不会抛出 OutOfMemoryException
我在 Windows(64 位)和 Linux(32 位)上尝试了下面的代码。
我确信如果没有 BufferedOutputStream,代码必然会抛出 OutOfMemoryException,但事实并非如此。
这是为什么?谁在磁盘上进行{缓存/缓冲/流化}?
如果与答案相关,您能否描述一下完整的流程(Java API -> 系统调用)?
这段代码使用了NIO吗?
/我很困惑。
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteHugeFileToDisk {
private static int BYTE = 1;
private static int KILBYTE = BYTE * 1024;
private static int MEGABYTE = KILBYTE * 1024;
private static int GIGABYTE = MEGABYTE * 1024;
private static long TERABYTE = GIGABYTE * 1024L;
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(args[0]);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
byte[] buffer = new byte[MEGABYTE];
for(int i = 0; i < buffer.length; i++) {
buffer[i] = (byte)i;
}
for(long l = 0; l < 4000; l++) {
dataOutputStream.write(buffer);
;
}
}
}
我已经使用 Java 6 运行了此代码。使用以下调用:
Windows:
java WriteHugeFileToDisk %TEMP%\hi.txt
Linux:
java WriteHugeFileToDisk /mnt/hi.info
请注意:该代码创建了 4GB 的文件,仅用于测试。
I've tried the code below on both Windows (64bit) and Linux(32bit).
I was sure that without BufferedOutputStream the code is bound to throw OutOfMemoryException yet it didn't.
Why is that? Who is doing the {caching / buffer / steaming} to disk there?
Can you please describe, if relevant to the answer, the full flow (Java API -> system call) ?
Does this code uses NIO?
/Me confused.
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteHugeFileToDisk {
private static int BYTE = 1;
private static int KILBYTE = BYTE * 1024;
private static int MEGABYTE = KILBYTE * 1024;
private static int GIGABYTE = MEGABYTE * 1024;
private static long TERABYTE = GIGABYTE * 1024L;
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream(args[0]);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
byte[] buffer = new byte[MEGABYTE];
for(int i = 0; i < buffer.length; i++) {
buffer[i] = (byte)i;
}
for(long l = 0; l < 4000; l++) {
dataOutputStream.write(buffer);
;
}
}
}
I've ran this code with Java 6. Using the following invocations:
Windows:
java WriteHugeFileToDisk %TEMP%\hi.txt
Linux:
java WriteHugeFileToDisk /mnt/hi.info
Please note: The code creates 4GB file full of just for the test.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
为什么它会抛出
OutOfMemoryException
?它只是写入磁盘。如果FileOutputStream
和DataOutputStream
有一些缓冲(我没有检查),我不会感到惊讶,但它们肯定不需要缓冲你写的一切。这段代码没有直接使用 NIO,尽管如果某些内部内容使用了 NIO,我也不会感到惊讶。至于涉及哪些系统调用以及何时进行 - 这将是特定于实现的,但重要的是要认识到
DataOutputStream
和FileOutputStream
都不是为了缓冲所有内容。您向它们写入一些数据,其中一些数据可能会写入磁盘。如果您刷新或关闭流,那么您迄今为止写入的所有数据都应该到达磁盘。如果您不刷新或关闭流,我预计只会缓存相当少量(同样,特定于实现的)(如果有的话)。请注意,
BufferedOutputStream
确实引入了缓存 - 但仅限于您要求的数量(或默认值)。同样,它不会缓冲所有内容,除非您要求与写入数据一样多的缓冲区。Why would it throw an
OutOfMemoryException
? It's just writing to disk. I wouldn't be surprised ifFileOutputStream
andDataOutputStream
had some buffering (I haven't checked) but they're certainly not required to buffer everything you write.This code isn't using NIO directly, although I wouldn't be surprised if some of the internal stuff did. As for what system calls are involved and when - that will be implementation specific, but the important thing is to realise that neither
DataOutputStream
norFileOutputStream
are meant to buffer everything. You write some data to them, and some of that data may get written to disk. If you flush or close the stream, that should make all the data you've written so far get to the disk. If you don't flush or close the stream, I'd expect only a reasonably small amount (again, implementation-specific) to be cached, if any.Note that
BufferedOutputStream
does introduce caching - but only as much as you ask for (or a default). Again, it wouldn't buffer everything unless you asked for as much buffer as you write in terms of data.这两条指令几乎不消耗内存并打开文件句柄。
分配并填充 1MB 数据存储在内存中的字节数组。
将 1MB 数据写入输出文件 4000 倍。
结论:消耗了 1MB 内存,并将 4GB 数据写入文件。因此,除非你的内存很少,否则不会抛出
OutOfMemoryException
。Those two instructions consume almost no memory and open a file handle.
Allocate and fill with 1MB of data a byte array which is stored in memory.
Write to the output file 4000 times this 1MB of data.
Conclusion : 1MB of memory is consumed and 4GB of data written to a file. So unless you have very little memory this cannot throw
OutOfMemoryException
.缓冲流是一个流包装器(很明显),它在将数据传递到底层流之前将数据缓冲到内存中。当与文件流结合使用时,这可以为您提供更好的性能,因为读取或写入硬盘驱动器会产生大量开销。缓冲允许您将低效的多次读取或写入压缩为单个高效的更大读取或写入次数,从而显着减少读取/写入次数。但是,它对于应用程序的正常运行并不至关重要。它只是帮助您减少对物理设备的访问。
Java 并不比其他语言更直接地访问您的计算机设备。在你的程序和硬盘上的位之间,仍然有几个层有权缓冲或缓存 Java 拼命试图从磁盘获取或写入磁盘的任何内容。据我所知,操作系统可以(并且通常会)缓存或缓冲内容,并且某些硬件也会这样做。
缓冲,在 Java 操作的含义中,与对设备或任何流的读取或写入的成功或失败无关。
A buffered stream is a stream wrapper that (quite obviously) buffers data into memory before passing it to the underlying stream. This gives you better performances when used in conjunction with a file stream because there's a lot of overhead involved in reading or writing to a hard drive. Buffering allows you to significantly reduce the number of reads/writes by collapsing otherwise inefficient multiple reads or writes into a single, efficient, bigger one. However, it is not critical to the well-behaving of your application. It just helps you do less accesses to the physical devices.
Java doesn't have more direct access to your computer's devices than other languages. Between your program and the bits on your hard disk, there still are several layers that are entitled to buffer or cache whatever Java desperately tries to get from or to the disk. As far as I know, the OS can (and usually will) cache or buffer stuff, and some hardware will do it too.
Buffering, in the Java meaning of the operation, has nothing to do with the success or failure of reads or writes to devices, or for that matter, to any stream.
没有人。它直接写入磁盘。没有任何增量内存使用。
Nobody. It is writing directly to the disk. No incremental memory usage whatsoever.