OpenJ9 VM Internship: Inline Types

My name is Adithya Venkatarao and I’m a 4th year Computer Science student from the University of Waterloo at the time of writing. After working in a variety of back-end and full-stack positions throughout my previous internships, I really wanted to further diversify my skillset by taking a dive down the tech-stack to work with low-level concurrent systems. I found the opportunity to do so when I joined IBM’s Eclipse OpenJ9 team as a VM Developer where I would be working on significant initiatives that would shape the future of the Java language! I’d like to use this blog post to reflect on my four-month journey on the team and provide insight on some of the things that I was a part of. You can click here to find a summary of all of the pull requests that I’d contributed to the OpenJ9 repository, but I will also link specific commits and relevant links throughout the blog as I discuss some of my tasks in detail.

What Did I Work On?

Shortly after my orientation, I would come to learn that my area of focus would be integrating JVM support for inline type, an unreleased Java type that is to be introduced in a future release to join alongside traditional primitive types (int, byte, float, etc) and the reference type that have existed since Java’s inception. For anyone unfamiliar and/or unwilling to read through a long and thorough explanation, inline types allow users to create classes that can contain member fields and functions like reference types while avoiding the memory footprint costs that regular references need to pay to maintain identity and mutability. They are sometimes thought of as user defined primitives and were also previous referred to as value types.

Getting To Know OpenJ9

I was first tasked with writing test cases to ensure that static fields in inline type objects were behaving appropriately. To accomplish this seemingly simple issue, I used a Java bytecode manipulation library called ASM to implement new generator functions that would create inline types with static fields and perform operations on its fields to validate their behaviour. That quickly followed with another task where I had to modify the implementation of the CheckCast bytecode instruction to handle casting inline types to null appropriately. While these tasks were not too difficult in principle, the elevated learning curve of getting comfortable with a large and complicated JVM codebase was quite the beast to overcome. It was not long before I had resolved some bugs in the code that were leading to erroneous test outputs that I thought was happening due to mistakes with my code. It was an exciting prospect to realize that I wasn’t just allocated “easy” tasks to do, but rather played an important role as an active maintainer of the codebase. The first four weeks were vital in helping me develop a solid foundational understanding of the JVM architecture while gaining proficiency at using the Direct Dump Reader (DDR) to analyze the Java program state (eg. register values, bytecode translations) and using GDB in conjunction to analyze the native C execution of the JVM code.

Flattened Inline Type Support

One of my main initiatives was integrating flattening support for inline types. You can read this blog post to understand the differences between references and flattened inline types in memory, but in summary, flattening allow inline types in arrays to discard the header and reference in favour of directly embedding the object fields inline in order to use significantly less memory. Given the new memory model of flattened inline types in arrays, I enhanced DDR functionality to identify flattened arrays, calculated memory offsets given a field name and array index , and displayed the contents of the data structure. It was interesting to learn about the differences in memory layout between references and inline types while considering the underlying behaviour of memory management and allocation with different field alignments (single, double and object alignment). Debugging through direct analysis of hexadecimal values in memory blocks proved to be a unique experience and it felt rewarding to know that I played a role in enhance debugging software that is widely used by developers and users alike.

Furthermore, while the VM integration for flattening had recently been added, GC support was still in its developmental phase. This necessitated the implementation of various test cases that would help evaluate the correctness of the GC execution given different memory alignments with flattening both enabled and disabled. The task involved a fair amount of code tracing and analysis to ensure that the test case triggered the expected GC execution flow and only “broke” during the action of attempting to moving a flattened array in memory. These efforts ultimately led to the discovery of another bug in the VM where the stackmap was corrupted due to erroneous application of the aload0getfield bytecode optimization.

Inline Type Equality Check

If I were to single out a single initiative that is most memorable, it would definitely be my efforts on integrating inline type support for the ACMP bytecode: an instruction that is executed when an equality check using the “==” operator is initiated. While traditional checks between two object references compare their addresses to compare identity, inline types require a recursive field by field equality check to determine whether all the member fields between the two inline types are equal. In particular cases, an optimization could be applied to directly compare blocks of memory that represent the inline type objects. It was really interesting to implement the bytecode logic from scratch while considering various things like strictly abiding by official Java specifications (including quirks with the “not a number” state), padding size differences given different memory alignments, code path contention considerations to efficiently reorder execution logic and applying GC barriers when reading memory address to ensure that there aren’t any issues if a concurrent GC thread decides to move an object while another mutator thread is attempting to read it.

Final Thoughts

The problems that I worked to solve while on the OpenJ9 team were definitely one of the more challenging experiences in my professional career. I was given a real opportunity to work on important Java features and loved learning more about low level C programming and the Java ecosystem while doing so. I’d like to thank my mentor Tobi Ajila for his guidance and the rest of OpenJ9 team for making the experience rewarding and memorable!

Leave a Reply