Tuesday, 11 August 2015

How System.out.println() really works

  • Commonly asked by Java beginners is the question, “How does System.out.println() work?”; 
  • Most of the relevant code can be found on OpenJDK, which is the primary implementation of Java itself. System.out.println() may be implemented completely differently on other Java platforms.

I will warn you now that this article is very long and not for the easily bored.

First steps

  • Our first step in figuring out how System.out.println works is by first understanding what System.out is and how it came to be.
  • Let’s take a look through the OpenJDK’s Mercurial online repository. Digging around a bit, we find System.java. In this file System.out is declared:

public final static PrintStream out = nullPrintStream();

But when we find the code for nullPrintStream():

private static PrintStream nullPrintStream() throwsNullPointerException{      
if (currentTimeMillis() > 0) {        
  return null;    
    }    
  throw new NullPointerException();

}


  • So nullPrintStream() simply returns null or throws an exception. This can’t be it. What’s going on here?

The answer can be found in the function initializeSystemClass(), also in System.java:

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);

setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));

  • There’s a lot of stuff going on in this code. I’m going to refer back to this two lines of code later, but setOut0() is what actually initializes System.out.
  • The function setOut0() is a native function. We can find its implementation in System.c:
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
    jfieldID fid =
        (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
    if (fid == 0)
        return;
    (*env)->SetStaticObjectField(env,cla,fid,stream);
}

  • This is pretty standard JNI code that sets System.out to the argument passed to it.
  • At first all this deal with setting System.out to nullPrintStream() and later setting it with JNI seems entirely unnecessary. But this is actually justified.
  • In Java, static fields are initialized first, and everything else comes after. So even before the JVM and the System class is fully initialized, the JVM tries to initialize System.out.
  • Unfortunately at this point the rest of the JVM isn’t properly initialized so it’s impossible to reasonably set System.out at this point. The best that could be done would be to set it to null.
  • The System class, along with System.out is properly initialized in initializeSystemClass() which is called by the JVM after static and thread initialization.


There is a problem, however. System.out is final, meaning we cannot simply set it to something else in initializeSystemClass(). There’s a way around that, however. Using native code, it is possible to modify a final variable.

No comments: