System.out.println()标准输出方法性能影响一窥
程序员文章站
2022-04-09 22:21:33
...
System.out.println()标准输出方法性能影响一窥
#
以前在写功能性代码的时候就知道,代码功能性的强大往往意味着性能的丢失。
那么非常好用支持任何格式输出到控制台的System.out.println()标准输出方法究竟是如何工作的呢?
做一个简单的测试
public class TestOut {
private static long timeOut = System.currentTimeMillis() + 1000L;
private static void outQuick() {
long i = 1L;
while (timeOut >= System.currentTimeMillis()) {
i++;
}
System.out.println(i);
}
private static void outOnlyNumber() {
long i = 1L;
while (timeOut >= System.currentTimeMillis()) {
System.out.println(i++);
}
}
private static void outOnlyString() {
long i = 1L;
while (timeOut >= System.currentTimeMillis()) {
System.out.println(""+i++);
}
}
public static void main(String[] args) {
outQuick();
//outOnlyNumber();
//outOnlyString();
}
}
首先测试的没有使用标准输出方法的运算,得到的结果稳定在2亿4千万次到2亿七千万次左右,下面给出几次结果
256908210
241685465
271216750
259256219
第二次测试的打印数字,执行结果大概在17万到25万之间,其中大多数为17万左右
170775
160830
175684
251080
第三次测试打印把数字转成字符然后再输出,执行结果稳定在20万以上
276857
252494
230742
291023
得到以下几条信息
- System.out.println()标准输出方法对性能的影响导致执行效率下降了1500倍左右
- System.out.println()标准输出方法使用字符串输出时执行效率只下降1000倍左右
为什么会这样呢?
看看源码,发现System.out.println()标准输出方法的执行过程是这样的
/** * 参数不同会调用不同的构造方法
* Prints a String and then terminate the line. This method behaves as
* though it invokes <code>{@link #print(String)}</code> and then
* <code>{@link #println()}</code>.
*
* @param x The <code>String</code> to be printed.
*/
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
/** * 如果是一个对象,则会多一句代码String s = String.valueOf(x);
* Prints an Object and then terminate the line. This method calls
* at first String.valueOf(x) to get the printed object's string value,
* then behaves as
* though it invokes <code>{@link #print(String)}</code> and then
* <code>{@link #println()}</code>.
*
* @param x The <code>Object</code> to be printed.
*/
public void println(Object x) {
String s = String.valueOf(x);
// 该方法是一个synchronized的方法,首先打印字符,然后换一行。
synchronized (this) {
print(s);
// newLine()也是一个synchronized的方法
newLine();
}
}
/** * 多出的这一句代码实质上是调用了对象的toString()方法并做了空判断
* Returns the string representation of the {@code Object} argument.
*
* @param obj an {@code Object}.
* @return if the argument is {@code null}, then a string equal to
* {@code "null"}; otherwise, the value of
* {@code obj.toString()} is returned.
* @see java.lang.Object#toString()
*/
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
/** * 也许你以为newLine()方法只是打印一个\n,但是实际上,他却是这样的
* 其中textOut.flushBuffer();也是一个synchronized方法
* 如果在这个过程中发生了IO中断异常,newLine()方法会中断掉当前线程
*/
private void newLine() {
try {
synchronized (this) {
ensureOpen();
textOut.newLine();
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
/** * 将输出缓冲区刷新到基础字符流
* Flushes the output buffer to the underlying character stream, without
* flushing the stream itself. This method is non-private only so that it
* may be invoked by PrintStream.
*/
void flushBuffer() throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar == 0)
return;
out.write(cb, 0, nextChar);
nextChar = 0;
}
}
/** * 打印字符,如果字符不为空的话
* Prints a string. If the argument is <code>null</code> then the string
* <code>"null"</code> is printed. Otherwise, the string's characters are
* converted into bytes according to the platform's default character
* encoding, and these bytes are written in exactly the manner of the
* <code>{@link #write(int)}</code> method.
*
* @param s The <code>String</code> to be printed
*/
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
/** * 输出字符的write方法和newLine动作其实是一致的
* System.out.println()标准输出方法其实相当于print()方法调用两次
*/
private void write(String s) {
try {
synchronized (this) {
ensureOpen();
// textOut是BufferedWriter的一个对象,该类继承至Object以及Writer类
// 该类是一个抽象类,作用是向字符流中执行写入。
textOut.write(s);
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush && (s.indexOf('\n') >= 0))
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
/** * textOut.write(s);调用的是该方法
* 但是该方法只是把字符拆分一些信息来传递参数
* Writes a string.
*
* @param str
* String to be written
*
* @throws IOException
* If an I/O error occurs
*/
public void write(String str) throws IOException {
write(str, 0, str.length());
}
/** * 该方法也是synchronized的
* lock其实是this,通过this.lock=this获得
* Writes a portion of a string.
*
* @param str
* A String
*
* @param off
* Offset from which to start writing characters
*
* @param len
* Number of characters to write
*
* @throws IndexOutOfBoundsException
* If <tt>off</tt> is negative, or <tt>len</tt> is negative,
* or <tt>off+len</tt> is negative or greater than the length
* of the given string
*
* @throws IOException
* If an I/O error occurs
*/
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= WRITE_BUFFER_SIZE) {
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else { // Don't permanently allocate very large buffers.
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}