Unveiling Ballerina GraalVM Image: Tackling Production Issues
- Krishnanathalingam Tharmigan
- Senior Software Engineer - WSO2
This article is based on Ballerina Swan Lake Update 7 (201.7.0).
Within this article, we will delve into the process of integrating profiling and monitoring tools with the Ballerina GraalVM image. The Ballerina GraalVM image, an enhanced version of a Ballerina application that runs as a native executable, brings significant improvements in performance and memory consumption. However, like any production application, it may encounter issues that affect its reliability and performance. This article delves into the tools and techniques for analyzing and resolving production issues specific to the Ballerina GraalVM image. We'll focus on generating heap dumps and connecting a Ballerina GraalVM native application to Java Flight Recorder (JFR) to diagnose and address problems effectively.
Understanding Ballerina GraalVM Image:
The Ballerina GraalVM Image is a native executable of a Ballerina application created with the GraalVM native image tool. It leverages GraalVM's capabilities to compile Ballerina code into a standalone executable, delivering improved performance and reduced memory usage. To fully harness the advantages of the Ballerina GraalVM Image, it's crucial to be aware of potential production issues that may impact its performance.
In a production environment, various unexpected issues can affect the performance and reliability of the Ballerina GraalVM Image. Common challenges include memory leaks, deadlocks, and performance bottlenecks. Understanding and resolving these issues is essential for ensuring the smooth operation of applications built with the Ballerina GraalVM Image.
Diagnosing and Resolving Issues:
To address production issues, we rely on specific tools. Two indispensable tools for diagnosing and resolving problems with the Ballerina GraalVM Image are heap dumps and Java Flight Recorder (JFR). Heap dumps provide snapshots of an application's memory, capturing information about object allocation and memory usage. JFR, on the other hand, is a powerful profiling and diagnostic tool that offers detailed runtime information.
In this article, we will explore how to effectively utilize these tools with a Ballerina GraalVM Image. We will discuss how to generate heap dumps and demonstrate the process of connecting a Ballerina GraalVM native application to Java Flight Recorder. To illustrate the use of these tools, we will work with a simple Ballerina program that may encounter an out-of-memory issue. This practical example will guide us through the diagnosis and resolution process.
To follow along, please complete the following steps:
- Download and install Ballerina Swan Lake 2201.7.0 or a newer version.
- Install GraalVM and configure it correctly.
- Install Visual Studio Code with the Ballerina extension.
- Open the command terminal in Visual Studio Code and run the following command to create a Ballerina application:
Navigate to the main.bal file within the ballerina_oom folder and replace the existing content with the following:
In this sample, we will cover the following topics:
- Generating heap dumps
- Connecting to Java Flight Recorder
Generating Heap Dumps with the Executable:
You can create a heap dump of a running Ballerina GraalVM executable to monitor its execution. This heap dump is similar to what you'd get for a Java application and can be viewed using the VisualVM tool.
To enable heap dump support, the Ballerina GraalVM executable should be built with the --enable-monitoring=heapdump option. Currently, there are two ways to create heap dumps:
Method 1: Create Heap Dumps by Sending a SIGUSR1 Signal at Runtime:
GraalVM's native image doesn't support generating heap dumps using the Attach API yet. So, tools like jmap or jcmd won't work. However, you can send a SIGUSR1 signal to the process to automatically generate heap dumps for you.
To do this, let's build the Ballerina application with the --enable-monitoring=heapdump option. Run the following command within the ballerina_oom folder.
Note: You can also add native image options in the Ballerina.toml. For more details, refer to the "Configure GraalVM Native Image Build Options" section.
Running the Executable:
Execute the built executable from the target/bin directory.
To find the process ID of the application, run the following command:
The process ID (PID) will be listed, like this:
Send the SIGUSR1 signal to the process using this command:
The heap dump will be created with a name like svm-heapdump-<process-id>-<timestamp>.hprof. You can open this heap dump using VisualVM or Eclipse Memory Analyzer.
Dumping the Initial Heap:
Now, let's explore the initial heap of the native image to see which objects are generated during the native image build process. Run this command:
The initial heap will be dumped as ballerina_oom.hprof in the current directory.
Figure 1: Heapdump opened in VisualVM
Creating Heap Dumps with VisualVM:
A user-friendly method to generate heap dumps is by utilizing VisualVM. To do this, you should build with the --enable-monitoring=heapdump,jvmstat native image option. This additional jvmstat option enables VisualVM to identify and list running native executable processes. You can request heap dumps in a manner similar to Java processes (simply right-click on the process and select "Heap Dump").
Let's build the Ballerina GraalVM executable with heap dump and jvmstat enabled. Run the following command within the ballerina_oom folder:
Run the executable and check the process in the VisualVM window
Figure 2: Monitoring the native-image process in VisualVM
For more information on generating a heap dump for a GraalVM native image, please refer to "Create Heap Dump."
- Generating a heap dump for a GraalVM native image is not supported on Windows.
- Generating a heap dump when an Out-Of-Memory error occurs is not yet supported with GraalVM images. For additional details, consult this GitHub issue.
Using Runtime Arguments:
The Ballerina GraalVM executable supports various runtime arguments. To view them all, execute the native executable with the following argument: -XX:PrintFlags=.
Connecting to Java Flight Recorder (JFR):
To create a Ballerina GraalVM executable with JFR event support, you must build it with the --enable-monitoring=jfr option. Additionally, at runtime, you can initiate a recording and configure logging using the following options:
- -XX:+FlightRecorder: Enable JFR at runtime.
- -XX:StartFlightRecording: Start a recording when the application starts.
- -XX:FlightRecorderLogging: Configure log output for the JFR system.
To build the Ballerina GraalVM executable with VM inspection enabled, run the following command within the ballerina_oom folder:
Let's run the Ballerina executable while starting a recording on the startup (to stop the process with OOM, we can set the max heap size at the runtime using the -XX:MaxHeapSize= option).
The recording will be created in the current directory with the name specified. To view the contents of the recording file, run this command:
The recording can be opened using VisualVM as well.
We can also incorporate JDK Mission control with JFR to monitor events, start flight recorders and generate heap dumps by specifying the --enable-montioring=all option at build-time. This option will enable heapdump, jvmstat and jfr.
Figure 3: Monitoring the native-image process in JDK Mission Control
Figure 4: The JFR created for the native-image process
In summary, the Ballerina GraalVM Image empowers developers with improved performance and reduced memory usage for their Ballerina applications. However, like any production environment, challenges can surface, affecting the performance and reliability of these applications.
In this article, we've explored common production issues that may arise when using the Ballerina GraalVM Image and introduced essential tools for diagnosing and resolving these issues. By gaining a comprehensive understanding of these production challenges and employing tools such as heap dumps and JFR, developers can overcome obstacles, enhance performance, and ensure the success of their Ballerina GraalVM Image applications.
For more information on running a GraalVM native image with JFR, please refer to "Build and Run Native Executables with JFR."