Chapter 12. Java SE API Tips
This chapter covers areas of the Java SE API that have implementation quirks that affect
their performance. There are many such implementation details throughout the JDK; these
are the areas where I consistently uncover performance issues (even in my own code).
When I joined the Java Performance Group in 2000, my boss had just published the first ever
topic on Java performance, and one of the hottest topics in those days was buffered I/O.
Fourteen years later, I was prepared to assume the topic was old hat and leave it out of this
topic. Then, in the week I started this topic's outline, I filed bugs against two unrelated pro-
jects where unbuffered I/O was greatly hampering performance. A few months later, as I was
working on an example for this topic, I scratched my head as I wondered why my “optimiza-
tion” was so slow. Then I realized: stupid, you forgot to buffer the I/O correctly.
So let's talk about buffered I/O performance. The InputStream.read() and Out-
putStream.write() methods operate on a single character. Depending on the resource they
are accessing, these methods can be very slow. A FileInputStream that uses the read()
method will be excruciatingly slow: each method invocation requires a trip into the kernel to
fetch 1 byte of data. On most operating systems the kernel will have buffered the I/O and so
(luckily) this scenario doesn't trigger a disk read for each invocation of the read() method.
But that buffer is held in the kernel, not the application, and reading a single byte at a time
means making an expensive system call for each method invocation.
The same is true of writing data: using the write() method to send a single byte to a
FileOutputStream requires a system call to store the byte in a kernel buffer. Eventually
(when the file is closed or flushed), the kernel will write out that buffer to the disk.
For file-based I/O using binary data, always use a BufferedInputStream or BufferedOut-
putStream to wrap the underlying file stream. For file-based I/O using character (string)
data, always wrap the underlying stream with a BufferedReader or BufferedWriter .