This documentation is for an old GraalVM version. See the latest version.

Build and Run Native Executables with Remote JMX

Remote management using Java Management Extensions (JMX) is possible in executables built with GraalVM Native Image.

Note: The feature is experimental.

This guide covers the steps required to build, run, and interact with such a native executable using JMX. It also shows you how to register a custom managed bean (MBean) with the JMX server and the additional steps required for it to work with Native Image.

Currently Supported Features and Limitations #

A JMX connection from a client to a remote MBean server is supported. The client, the server, or both may be a native executable. Only MXBeans, and standard user-defined MBeans, are supported. Dynamic and model MBeans are not supported because their management interfaces are defined at run time. Although remote management of MXBeans is supported, not all platform MXBean functionality is implemented or is applicable in Native Image. Additionally, to define and use standard MBeans, you must specify metadata configuration. This is further explained in this guide.

Step 1: Create a Demo Application #

Create a demo application in a directory named demo. Change your working directory to there and run the commands from that directory.

Save the following code to a file named SimpleJmx.java. The main() method registers a custom MBean, then loop endlessly, so you have time to inspect the process using VisualVM.

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class SimpleJmx {
    public static void main(String args[]) throws Exception {
        ObjectName objectName = new ObjectName("com.jmx.test.basic:name=simple");
        Simple simple = new Simple();
        simple.setName("someName");
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        server.registerMBean(simple, objectName);
        while (true) {
            Thread.sleep(1000);
            System.out.println("JMX server running...");
        }
    }

    public static interface SimpleMBean {
        String getName();

        void setName(String name);

        String print();
    }

    static class Simple implements SimpleMBean {
        private String name;

        @Override
        public String getName() {
            return name;
        }

        @Override
        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String print() {
            return "Print output " + name;
        }
    }
}

Step 2: Compile to Java Bytecode #

  1. Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.

  2. Compile the Java file using the GraalVM JDK:

     $JAVA_HOME/bin/javac SimpleJmx.java
    

    This creates SimpleJmx.class, SimpleJmx$Simple.class, and SimpleJmx$SimpleMBean.class files.

Step 3: Make a Dynamic Proxy Configuration #

JMX uses dynamic proxies, a dynamic feature of Java, to access MBeans. To be able to interact with the custom SimpleMBean at run time, you need to provide Native Image with additional dynamic proxy configuration for the MBean interface. For this, create a JSON file named proxy-config.json with the following contents:

[
  { "interfaces": [ "SimpleJmx$SimpleMBean"] }
]

In the next step, we will pass this JSON file to the native-image builder.

Step 4: Build a Native Executable with JMX Support #

Build a native executable with VM monitoring enabled:

$JAVA_HOME/bin/native-image --enable-monitoring=jmxserver,jvmstat  -H:DynamicProxyConfigurationFiles=proxy-config.json SimpleJmx

The --enable-monitoring=jmxserver option enables the JMX Server feature which allows accepting incoming connections. The --enable-monitoring=jmxclient option enables the JMX Client feature which allows making outgoing connections. Both features can be used together, comma-separated, for example, --enable-monitoring=jmxserver,jmxclient. The jvmstat option should also be included if you want to enable discovery by VisualVM and other JVMs: --enable-monitoring=jmxserver,jmxclient,jvmstat.

Step 5: Run the Executable with JMX Properties #

Now run your native executable with JMX properties:

./simplejmx -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=9996 -Dcom.sun.management.jmxremote.ssl=false

This starts the application as a simple JMX server, without password authentication or SSL using port 9996. You can configure JMX to apply all the usual properties as shown in this guide, but this example uses a basic configuration for simplicity.

Step 6: Inspect Using VisualVM #

  1. Start VisualVM to view the managed beans in a user-friendly way. Note that VisualVM is shipped separately and should be first added to GraalVM using gu, and then started:

     $JAVA_HOME/bin/gu install visualvm
     $JAVA_HOME/bin/visualvm
    
  2. Make sure you have the “VisualVM-MBeans” plugin installed (Tools > Plugins > Available Plugins > select “VisualVM-MBeans” and click Install).

  3. Go to the Applications tab and select the SimpleJmx process. From there you can select the MBeans tab.

    Remote JMX

  4. In the MBeans tab, you can inspect the custom MBean you created earlier and perform operations on it.

    Custom MBean Attributes

    Custom MBean Operations

To conclude, Native Image now provides support for remote management using JMX. Users can enable the JMX agent in a native executable to monitor a client application running on a remote system.

Connect with us