2

From the PrintStream documentation:

Optionally, a PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written.

Then given code

System.out.print("hi");   // gives console output: hi
System.out.print(7);      // gives console output: 7

// prevents flushing when stream wiil be closed at app shutdown
for (;;) {
}

Why then I see output to my console? Nothing shall be written to console (PrintStream instance from System.out), because nothing shall be flushed so far!

This didn't answer this.

I guess, the answer is in the source code (private utility method BufferedWriter.flushBuffer()), but I don't understand the comment to code: "Flushes the output buffer to the underlying character stream, without flushing the stream itself": if PrintStream (which is tied to console output), which is "stream itself" is not flushed, output to console shall not be refreshed!...

Source for PrintStream.print(String):

private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                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;
        }
    }

Source for BufferedWriter.flushBuffer():

/**
     * 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;
        }
    }

More details are also given here. It is very complicated, but seems like at some stage BufferedWriter is given to PrintStream constructor.

LrnBoy
  • 345
  • 2
  • 7

2 Answers2

2

I went step by step using debugger and this is what I found: enter image description here String s is displayed in the console after 527th line, so it's before line 528 in which the check of having \n is done.

In charOut.flushBuffer() deep inside, there is the following method called:enter image description here

In which, the check about \n is missing.

The flow is as it follows:

  1. System.out#print(String s) calls PrintStream#print(String s).
  2. PrintStream#print(String s) calls PrintStream#write(String s).
  3. PrintStream#write(String s) calls OutputSteamWriter#flushBuffer().
  4. OutputStreamWriter#flushBuffer() calls StreamEncoder#flushBuffer().
  5. StreamEncoder#flushBuffer() calls StreamEncoder#implFlushBuffer().
  6. StreamEncoder#implFlushBuffer() calls StreamEncoder#writeBytes().
  7. StreamEncoder#writeBytes() calls PrintStream#write(byte buf[], int off, int len) which flushes the buffor if(autoFlush).

The most important snippets are above. The BufferedWriter seems not to be called in this flow.

xenteros
  • 14,275
  • 12
  • 47
  • 81
  • That's exactly what the OP is asking about - why is that affecting things in this case? – Oliver Charlesworth Jun 07 '17 at 09:09
  • API says, that if auto-flush is set to true, then only println is flushed, not print (!!!). I quote apidoc on PrintStream (its first paragraphs): "Optionally, a PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written." – LrnBoy Jun 07 '17 at 09:17
  • I also guess maybe its auto-flush is set to true, but could anyone please point to the place where it happens? I cannot find this place so far... – LrnBoy Jun 07 '17 at 09:19
  • @LrnBoy I added in deep explanation. – xenteros Jun 07 '17 at 10:38
0

https://bugs.openjdk.java.net/browse/JDK-8025883 describes this bug.

This bit me in a program that reads and parses a binary file, doing a lot of System.out.printf() calls, which took way longer that it should.

What I ended up doing was writing a helper class that violates the contract of Streams by not honoring every flush request:

class ForceBufferedOutputStream extends OutputStream {

    OutputStream out;
    byte[] buffer;
    int buflen;
    boolean haveNewline;
    private static final int bufsize=16384;

    public ForceBufferedOutputStream(OutputStream out) {
        this.out=out;
        this.buffer=new byte[bufsize];
        this.buflen=0;
        this.haveNewline=false;
    }
    @Override
    public void flush() throws IOException {
        if (this.haveNewline || this.buflen==bufsize) {
            out.write(buffer, 0, buflen);
            out.flush();
            this.buflen=0;
            this.haveNewline=false;
        }
    }
    @Override
    public void close() throws IOException {
        out.close();
    }

    @Override
    public void write(int b) throws IOException {
        buffer[buflen++]=(byte)b;
        if (b=='\n')
            this.haveNewline=true;
        if (buflen==bufsize)
            this.flush();
    }
}

then using a new PrintStream(new ForceBufferedOutputStream(System.out)) instead of System.out.

I consider this a horrible piece of software - as said, it violates the contract that flush() needs to make sure everything is written, and it could optimize array write calls. But in my case, runtime was cut from 17 minutes to 3:45, so if you need a copy/paste that speeds up a quick and dirty type of program, I hope it helps somewhat.

Guntram Blohm
  • 9,212
  • 2
  • 20
  • 28