Periklis Ntanasis:
Master's Touch

fade out

Code Reading #1: Java Collections - Compiling the JDK from Source Code

I am very excited that I am writing this article. This blog serves as a medium for me to explore new things, learn and have fun. This article will be the first of a series of articles where I am aiming to study the Java Collections Framework’s source code.

The idea behind this work is inspired by Spinellis' Code Reading: The Open Source Perspective book where the author argues that code reading is an essential skill which greatly benefits programmers and reading good code enables them to write better code themselves. It is also inspired by Java Champion Dr. Heinz Kabutz who in various occasions during demonstrations dives into JDK source code and explores how it is ticking under the hood. Finally, it is inspired by Josh Bloch who is known for his work on the Java Collections Framework and occasionally makes references to it in his excellent Effective Java book.

This series of articles will go through the OpenJDK source code and specifically the Java Collections Framework. I want to warn you, I have very limited time and preparing this material to a level I am happy to publish it takes much time. I will be happy if I can make it to publish once per year. So, don’t hang tight, just relax and enjoy the ride!

In this article I am going to set a basic environment for you to be able to go through the code the same way as I am while you are following me to this exciting journey!

Getting OpenJDK source code

Let’s get the OpenJDK source code by cloning the git repo from GitHub.

git clone https://github.com/openjdk/jdk

Selecting a version to explore

For the purposes of this article we are going to explore the version 18 of Java. The main reason we are going to do so is to keep things simple if we want to build the JDK.

First of all in order to build the JDK you need to have installed a previous version of the JDK which works as a bootstrap version used to build the next one. So, in order to build the 18 version you are going to need to have installed the 17 version. Older versions may work too but the safest bet is to use the immediate previous version. The 17 version is an LTS version, so it is a version which is usually relatively easy to install.

Furthermore, building the JDK requires other dependencies. Older versions of the JDK may have compatibility issues with newer versions of these dependencies and using older versions for the dependencies won’t be so easy.

For all these reasons we are going to do our exploring using version 18.

So, do the following.

git checkout jdk-18-ga

Building the project

There is a detailed build guide under doc/building.html you may consult if you run into trouble.

I personally use Linux (OpenSuse Tumbleweed). I had a few missing dependencies but the configuration tool suggested what I was missing and I have proceeded without any issue.

Roughly the procedure is the following:

  1. bash configure

  2. make images

That was it! To verify that the build was successful perform the following steps:

  1. ./build/*/images/jdk/bin/java -version

  2. make run-test-tier1

Try a code change

In future articles we are going to read code. However, let’s see how we may perform a code change to the JVM. This may come handy later during our reading in case we want to test something.

Our first JDK code change

In the previous section we tested that the build is successful by executing the ./build/*/images/jdk/bin/java -version command which prints the Java version.

Let’s change this string to something else!

We will change the file src/java.base/share/classes/java/lang/VersionProps.java.template and specifically the private static void print(boolean err, boolean newln) method.

Let’s change the current body of the method and make it print the string I hacked the JVM! Hoo-ray!. Here is the change:

--- a/src/java.base/share/classes/java/lang/VersionProps.java.template
+++ b/src/java.base/share/classes/java/lang/VersionProps.java.template
@@ -203,41 +203,7 @@ class VersionProps {
     private static void print(boolean err, boolean newln) {
         PrintStream ps = err ? System.err : System.out;

-        /* First line: platform version. */
-        if (err) {
-            ps.println(launcher_name + " version \"" + java_version + "\""
-                       + " " + java_version_date
-                       + (isLTS ? " LTS" : ""));
-        } else {
-            /* Use a format more in line with GNU conventions */
-            ps.println(launcher_name + " " + java_version
-                       + " " + java_version_date
-                       + (isLTS ? " LTS" : ""));
-        }
-
-        /* Second line: runtime version (ie, libraries). */
-        String jdk_debug_level = System.getProperty("jdk.debug", "release");
-        if ("release".equals(jdk_debug_level)) {
-           /* Do not show debug level "release" builds */
-            jdk_debug_level = "";
-        } else {
-            jdk_debug_level = jdk_debug_level + " ";
-        }
-
-        String vendor_version = (VENDOR_VERSION.isEmpty()
-                                 ? "" : " " + VENDOR_VERSION);
-
-        ps.println(java_runtime_name + vendor_version
-                   + " (" + jdk_debug_level + "build " + java_runtime_version + ")");
-
-        /* Third line: JVM information. */
-        String java_vm_name    = System.getProperty("java.vm.name");
-        String java_vm_version = System.getProperty("java.vm.version");
-        String java_vm_info    = System.getProperty("java.vm.info");
-        ps.println(java_vm_name + vendor_version
-                   + " (" + jdk_debug_level + "build " + java_vm_version + ", "
-                            + java_vm_info + ")");
-
+       ps.println("I hacked the JVM! Hoo-ray!");
     }

 }

In order to test our change we have to build the project again. We may run the command make images again. This will finish faster than the first time because it will detect and build only the required part of the project.

Then go on and run again the ./build/*/images/jdk/bin/java -version command. Cool, isn’t it?

Tip As an exercise try to do the following. Change the JDK source code so the method System.out.println() will print the argument in reversed order. Then build it and run a simple Java program to see the change in action! If you want a tip see this changeset.

Collections source code

The Java Collections Framework is part of the java.util package and is located under the src/java.base/share/classes/java/util/ directory.

You may open the java.base module from your favorite IDE in order to navigate easily around while reading the code.

JavaDoc

Comments are part of the code. The Java source code is very well documented by comments which can be used to generate the JavaDoc.

While reading the source code we are going to read also the comments.

If you prefer you may read them online in html format here.

Another option would be to generate them locally by running the make docs command. In my machine which is running Linux the documentation is generated under build/linux-x86_64-server-release/images/docs/api/java.base/java/util/. This could differ depending your system.

Epilogue

I think everything is set to start reading code.

The Collections Framework defines interfaces and classes which represent groups of objects. In the heart of the framework there is the Collection interface which defines a common behavior for all the classes of the framework.

In the next part I plan to start reading the ArrayList which is one of the most used classes of the Collections Framework.

See you later! It’s going to be a looooooong ride but I think we are going to have great fun!

Comments

fade out