Shenandoah: A pauseless GC for OpenJDK

It’s been quite a while that I did not post anything. The reason is that I was busy with a very interesting new project in the last weeks, Shenandoah, a new GC for OpenJDK. Christine, a collegue of mine in Red Hat’s Java team, set out to build a next generation garbage collector for OpenJDK. I jumped into this project a while ago, and I’m very excited about it.

I guess I will not go into technical details in this post, but instead outline a few of the problems of existing garbage collectors, and Shenandoah’s goals and some highlevel ideas how to achieve them.

  • Pause times: this problem existed since the invention of GCs, and hasn’t been fully solved since then. The good old stop-the-world garbage collection is probably still in everybody Java programmers mind. Nowadays we have more modern garbage collectors, and most of them drastically reduce pause times, but nevertheless, pause times still exist, be it for evacuation of regions, for book keeping of references, etc. Shenandoah aims to reduce pause times even more. Realistically speaking, there will still be pause times, but we aim to minimize them as much as possible. Our goal is to reduce them to less than 10ms per GC cycle.
  • Scalability. The larger the heap gets, and the more processors/threads are working, the more garbage is produced. This means more work for the garbage collector to find and free all this garbage. In other words, the garbage collector needs to be able to keep up with ever-growing memory demands and processor capability. We are shooting for TB-sized heaps.

The idea now is that, in order to reduce pause times, the GC needs to do as much work as possible concurrently to mutator threads (mutators are all threads that modify the heap, i.e. the actual application running). In order to be scalable, the GC needs to be able to utilize that ever-growing processing power by doing as much work as possible using multiple threads in parallel. (Notice that the terminology is a bit confusing here. In the Java GC world, ‘concurrent’ often means ‘concurrently with mutators’ while ‘parallel’ means ‘using parallel worker threads to do GC work’).

While that sounds conceptually simple, I can tell you it’s not. Boy, are there a lot of hairy issues to resolve here. I think I will explain the ideas one blog post at a time here. Stay tuned!

FOSDEM 2013

The last couple of years, I had to pass FOSDEM, for various reasons. I am very very happy that I finally sorted things out for this years FOSDEM, and I’m ready to go (hotel and trains booked). I will do a presentation about my Shark & Zero work on Saturday afternoon, and co-present Thermostat with Mario on Sunday noon, both in the Java dev room.

FOSDEM is one of those rare events in my life about which I can say that it had a very significant impact. I wouldn’t be where I am today, if I hadn’t attended it a few years ago. I am very happy that I can finally go there again, and meet lots of old and new friends and collegues. See you there, and cheers!

Quick Cacio-Web howto

Today we released Caciocavallo 1.3. The release announcement can be found here.

However, what is the more important news is that after the release, I fixed Cacio-Web to work with the latest Cacio build and enabled it in the default build so it doesn’t fall to the wayside again. On popular request I would like to summarize how to get Cacio-Web running. Note that this currently only works on Linux (patches to enable this on other platforms are welcome!)

First of all, check out the source code (the cacio-web changes are not yet released):

hg clone http://hg.openjdk.java.net/caciocavallo/ng/ caciocavallo

Then build it (you need Maven!):

cd caciocavallo
mvn clean install

And finally you should be able to run with with something like this:

java -Dcacio.web.port=9091 -cp cacio-web/target/cacio-web-1.4-SNAPSHOT-jar-with-dependencies.jar:/home/rkennke/src/test/SwingSet2.jar net.java.openjdk.cacio.server.CacioServer

The -Dcacio.web.port parameter specifies on which port should Cacio-Web listen. Notice that the classpath needs to include your application (SwingSet2.jar in this case).

Then point your browser to a URL like this:

http://localhost:9091/SessionInitializer?cls=SwingSet2

Where the parameter cls specifies the (fully qualified) name of the main class of the application to start.

Please let me know if you run into any problems.

The return of the Shark, part II (howto)

So, finally, after some back and forth, the Shark fixes landed in hotspot-comp (thanks Twisti for reviewing and pushing me). It took a little while to sort out the new atomic operations in LLVM. If you want to play with it, you first need LLVM 3.2 (not the latest 3.1 release!):

svn co http://llvm.org/svn/llvm-project/llvm/branches/release_32/ llvm-3.2
cd llvm-3.2
./configure && make && make install

Then you need to check out hotspot-comp:

cd ..
hg clone http://hg.openjdk.java.net/hsx/hotspot-comp/
cd hotspot-comp
sh get-source.sh

Finally, I recommend you use my build script for Shark: place it in the toplevel directoy of hotspot-comp and modify all the env variables to your needs. Most importantly, change LLVM_CONFIG to point to your $LLVM_INSTALL_DIR/bin/llvm-config. Enjoy the Shark! 🙂

The return of the Shark

During the last couple of days, I’ve been working on fixing Shark for OpenJDK8. Shark is an awesome compiler backend for OpenJDK’s Hotspot JIT, originally written by Gary Benson. The idea is that, instead of generating target machine code dircetly, Shark would generate an intermediate representation (IR) for LLVM, and let LLVM’s JIT compiler generate the target machine code. The advantage being that it’s much easier to port to new platforms than writing your own C1 and/or C2 compiler for HotSpot. Unfortunately, Gary left Red Hat’s Java team a while ago which basically left Shark unmaintained. In the time since, several significant changes happened both in HotSpot and LLVM. After having fixed Zero for OpenJDK8 I thought I’d give Shark a try and spend a little time on it. The most problematic changes in LLVM or Hotspot have been:

  • Hotspot’s internal oop class hierarchy has been split into two: oops (all objects) and metadata (classes, methods, etc). This caused some major headaches because Shark code was basically assuming everything’s an object, and it was not always clear what actually to use: a Klass* or its Java mirror (basically an instance of java_lang_Class)? Etc etc.
  • A bunch of intrinsics for atomic operations (compare-swap, memory barrier, etc) have been remove from LLVM, and in their place now is a direct representation in the IR. Which actually makes it much nicer and easier to use in Shark.
  • Several smaller little things, to many and probably to little to list here.

With this, I can now do a full build of OpenJDK/Shark on x86, which I am quite proud of. It’s already successfully running a lot of code, but at the same time I am still hitting some bugs. After I have ironed them out I will propose the changes for inclusion in OpenJDK8 of course.

 

Hotspot Zero & OpenJDK8 & DaVinci

During the last couple of weeks, I brought the Zero (i.e. no-assembly) interpreter of OpenJDK up-to-date with OpenJDK8 and (most of) the DaVinci project. Zero is not usually built in standard and developer builds of OpenJDK, and thus often falls by the wayside when new features are developed in Hotspot. Various fixes needed to be made in order to be able to build and run Zero with the latest developments in OpenJDK8 and the DaVinci project again:

  • The Makefiles needed to be updated for copying the .debuginfo and/or .diz files into the right places.
  • Various small and fairly obvious changes needed to be made like renamed or extended method names. (Some of them are not only needed for Zero, even the X86 CPP interpreter was broken.)

With those changes in place (with help from Chris Phillips), it was possible to build Zero with OpenJDK8. The next big step for me was to make Zero build with the DaVinci patch set, in particular with the meth-lazy-7023639.patch. This turned out to be a fairly large refactoring. This patch introduced the so-called lambda-forms for method handles, which moves most of the invocation logic into Java code. The method handles now generate synthetic methods to implement the invocation logic on-the-fly and call that, instead of implementing all of it in the VM. The most important changes that I made was:

  • Rewrote the interpreter handler for the invokedynamic instruction. It now basically resolves the call site and the lambda form to call, and calls it.
  • Implement a handler for the new invokehandle instruction. This is a JVM-internal bytecode that is generated for calls to the various intrinsics (see below), which pushes an optional appendix parameter on top of the interpreter stack, which is then consumed by those intrinsics and is basically a pointer to the target method to call.
  • Implement 5 new intrinsics for MethodHandle: invokeBasic(), linkToStatic(), linkToSpecial(), linkToVirtual() and linkToInterface(). The first one is used to call into a lambda form using a polymorphic signature. The latter 4 call out to the target method and are basically signature-polymorphic versions of the various invoke* bytecodes.

With those changes in place I can now build a Zero version of OpenJDK8 with the DaVinci (MLVM) patches and all jtreg tests for java/lang/invoke are passing now. As a very welcome side effect, I was able to throw out a *lot* of convoluted code for invokedynamic support in the interpreter. The new code is fairly simple and straightforward (and thus, more maintainable) instead. The code is available from a personal repository for now (it’s a forest-clone of the upstream hotspot-comp forest, with MQ patch queues in hotspot and jdk modules). In order to check it out, do this (you need the mq extension):

 

hg clone http://icedtea.classpath.org/people/rkennke/hotspot-comp-zero/
cd hotspot-comp-zero
sh get-source.sh
cd hotspot
hg qpush -a
cd ../jdk
hg qpush -a
cd ..

Then build with Zero enabled, see my build script to get an overview of the build variables.

Update: If you’re only interested in the patches themselves, have a look at the patch repository.

Hacking Hotspot in Eclipse

I recently started working on Hotspot, with the goal of getting the Zero port up-to-date with respect to he recent developments in MLVM. In order to work most efficiently, I set up a work environment in Eclipse, and thought it’d be useful to share a little HOW-TO.

First of all, you need to install Eclipse/CDT if you haven’t already. This gives you a very powerful C/C++ development environment in Eclipse.

You can then open an OpenJDK source tree as C/C++ project by selecting: File -> New -> Other and then ‘C/C++’ -> ‘Makefile project with existing code’. Enter project name, e.g. OpenJDK8, and the path to the existing location. Select the appropriate ‘Toolchain for indexer Settings’ below. Click Finish.

Then open the project properties by right-clicking on the project, and selecting ‘Properties’. There we need to setup a couple of things.Under ‘C/C++ Build’ -> Environment, enter all environment variables that you would normally set on the command line for building OpenJDK. At the very least, you need ‘ALT_BOOTDIR’ and LANG=C. Under ‘C/C++ Build’, click the tab ‘Refresh Policy’ and remove the only path that is there (otherwise the whole workspace will be refreshed after a build, which takes looooong’). Optionally, add any paths under ‘build’ that you are interested in. Under ‘C/C++ General’ -> ‘Paths and Symbols’, select the tab ‘Source Location’ and remove the toplevel project path, and enter any source paths you are working with (e.g. hotspot/src). This limits what is visible to the indexer, etc. In order to take full advantage of Eclipse for debugging, I also changed ‘C/C++ Build’, ‘Behavior’ tab, replace ‘all’ with ‘debug_build’ This will normally do a debug build of OpenJDK, which means that you get all the symbols and no compiler optimizations in the binaries. In order to be able to load the symbols in gdb, add ‘ZIP_DEBUGINFO_FILES=0’ into the environment variables.Then click ‘Apply’ and ‘OK’ to close the settings dialog. Select ‘Project -> Build Project’ to launch the first build of OpenJDK in Eclipse.

Debugging with Eclipse is similarily straightforward, open Debug Configurations, add a new C/C++ application, set up its properties for the binary, arguments and environment variables (make sure you use a debug-build binary) and run the thing! Being able to fully debug in Eclipse, navigating stack, inspecting variables, setting breakpoints and stepping through the code is so much more useful than doing the same in plain GDB:

Debug Hotspot Zero with Eclipse