This post was written by Jiří Sedláček — a tooling expert on the GraalVM team and author of VisualVM.

GraalVM — a high-performance polyglot virtual machine from Oracle Labs — enables programmers to create applications combining multiple programming languages within a single process. While this opens a world of new exciting possibilities, it also brings several challenges from the tooling perspective — traditional tools are not ready to work with polyglot applications. Luckily, GraalVM comes with a bunch of tools supporting multi-language monitoring, debugging and profiling.

In this article we describe how to create and analyze heap content of a polyglot application with Graal VisualVM. Using simple Hello World snippets written in Java, JavaScript, Python, R and Ruby we show how to save a heap dump from HotSpot JVM, and to open and analyze it in Graal VisualVM heap viewer. We expect that you already have some knowledge of using Java heap viewers, as explaining the related terms and describing techniques to find memory leaks are not the topic of this article.

Setup

Graal VisualVM — the only tool capable of viewing polyglot heap dumps — is bundled with GraalVM. As a start, download GraalVM from the downloads page and extract it to $GRAALVM_HOME .

Besides standard Java, the downloaded GraalVM already supports running JavaScript code out of the box. In case you’re interested in heap analysis of the other languages — Python, R and Ruby — you can easily get the additional functionality using the Graal Updater utility:

$GRAALVM_HOME /bin/gu install python

$GRAALVM_HOME /bin/gu install r

$GRAALVM_HOME /bin/gu install ruby

Sample Application

For our article we create a simple Java class printing “Hello World” using various languages. While this is not real polyglot code — the languages don’t interact with each other — it creates some data structures for every guest language, which can be displayed in the heap viewer.

This is the content of the sample PolyglotTest.java which we’ll use for generating the heap dumps. Feel free to uncomment the additionally installed guest languages that interest you:

The first line of the main method prints “Hello World” from a standard Java code. On the following lines we create a polyglot context for running the guest languages and print “Hello World” from each of these languages. The last two lines pause the execution thread for five minutes, which should be enough time for us to take the heap dump before the process finishes.

Now let’s compile the code and run it just like any other Java application:

$GRAALVM_HOME /bin/javac PolyglotTest.java

$GRAALVM_HOME /bin/java PolyglotTest

Based on which languages were enabled, the process prints the appropriate “Hello World” messages and falls asleep.

Getting the Heap Dump

To create a heap dump from the PolyglotTest process, we use the Graal VisualVM tool. Alternatively you can use any other tool of your choice to dump the heap in the standard Java .hprof format, like jmap or JConsole etc. Just keep in mind that you still have to open the heap dump in Graal VisualVM to analyze the guest language structures.

To start Graal VisualVM use the following command, which is similar to starting the preceding Java VisualVM tool from JDK 6~8:

$GRAALVM_HOME /bin/jvisualvm

The tool starts and immediately shows the running PolyglotTest process in the Applications pane on the left side. Select the process node and invoke the Heap Dump action from its context menu. A heap dump from PolyglotTest is created and opened in the heap viewer.

Note that in case you need to obtain heap dump from a guest language REPL — js, graalpython, R or truffleruby — make sure it runs using the HotSpot JVM and not as a native image process. This can be achieved by passing the --jvm flag to the REPL launcher:

$GRAALVM_HOME /bin/js --jvm

$GRAALVM_HOME /bin/graalpython --jvm

$GRAALVM_HOME /bin/R --jvm

$GRAALVM_HOME/ bin/truffleruby --jvm

Analyzing the Heap Dump

Once a heap dump is opened in Graal VisualVM — either automatically after creating it right from the tool, or manually by opening an external file using File | Load… action from the main menu — we’re presented a Java heap viewer with all the common features: reports, histograms, incoming and outgoing references to objects, paths to GC roots and many other. The Java heap viewer also allows us to analyze the dumps using custom queries and scripts in the OQL Console and R Console.

But today we want to see the guest language heap structures. We proceed with JavaScript, but the same way you can analyze any other guest language of your choice. Let’s click the leftmost “Summary” toolbar button and select the JavaScript Heap scope. From now we can see just the JavaScript structures as if the application ran in a native JavaScript environment.

By default we’re presented a JavaScript heap summary. We can see the basic telemetry like heap size and total number of objects, and several reports of biggest types and objects by count and size. We can also generate a report of dominating objects.

Let’s click the Summary toolbar button again and switch to the Objects view for the JavaScript heap scope. It shows a histogram of JavaScript types. There are just a few of them produced by our code snippet, you’ll see much more in a typical JavaScript application. The types can be sorted by number of objects or by total size. Sorting by retained size computes the retained sizes first, if not already computed from the Summary view. The types can be filtered using the Type Filter at the bottom.

Where appropriate, objects display their logical values for easier identification (gray string next to the object identificator). Each object in the Objects view can be expanded to display its properties (fields, attributes etc. based on the language) and references, where available. Using context menu, the objects and types can be opened in a new tab for further analysis. The scope of displayed data can be easily lowered by switching to a subtree view using Pin buttons in the breadcrumbs stripe.

Looking at the toolbar, the leftmost dropdown button always switches the data view (Summary, Objects, Threads) or changes the language scope (Java Heap, JavaScript Heap etc.). The Preset dropdown controls which data is displayed — All Objects, Dominators or GC Roots. The Aggregation section changes aggregation of the currently displayed data — either aggregated by type or displaying the objects directly without any aggregation. Note that the Type Filter works also for the Objects aggregation — just the objects of the desired type(s) are displayed.

The Details section in the Objects view toolbar allows us to open separate selection-aware views to display Properties and References while traversing individual objects. Where available, the Preview view shows a more detailed representation of the selected object. In the current version it displays preview for JavaScript functions and Ruby procedures.

Besides the Summary and Objects views there’s also a third view for JavaScript and Ruby called Threads. It displays the actual stack traces at the point where the heap dump was taken. For our sample application this view is empty as the guest language code is too simple to create a stack representation. We’ve used a bit more complex JavaScript application for the following screenshot:

We can see a JavaScript thread including the stack frames, just like for a regular Java application. Where available, each stack frame displays its local objects. Local objects can be further expanded to analyze their outgoing and incoming references. Alternatively, the Threads view can be switched to a common HTML format using the Results switch in the toolbar.

Conclusion

In this article, we described methods to save heap content of a polyglot application into a .hprof file. We opened the heap dump in the Graal VisualVM heap viewer, limited the scope of displayed data to just the selected guest language in our example JavaScript and examined the heap viewer features and views.

We highly appreciate your feedback on the Graal VisualVM heap viewer, other tools and the GraalVM itself — both positive and negative. Feel free to leave your comments in the responses below this article, or file a feature request or report a bug on GitHub.