Profiling JVM applications inside containers

on , , 3 minutes reading

Measuring and profiling applications are one of those things you normally don’t do when when you need to do it is kind of a magical black art, especially if you are (like me) one of those persons who really love to move and do all your development and work in containers. Do you want to see how your JVM application is doing in a container orchestration and see if those small “tweaks” you did made a difference? Well, I recently had to and this is my simple blog post to remind me in the future what I need to do so I don’t spend time again searching in the web.

First, we need a good JVM profilers, there are plenty out there but personally I got used to VisualVM, a free tool integrating a bunch of command line stuff to do profiling and troubleshooting, go and grab it, it works in macOS, Linux and Windows.

One thing I have to point out is that VisualVM needs a JDK installed and it does not detects it automatically, so you have to configure the file visualvm.conf file (located in the etc folder) and set the visualvm_jdkhome value with the full path of your JDK (this does not necessarily needs to be the same path as the JRE you are going to profile).

Now it is time to configure your container to be profiled, imagine this is the current entrypoint of your container: java -jar app.jar, we now need to “slightly” modify the entrypoint to allow profiling by VisualVM, the following parameters need to be added:

  • -Dcom.sun.management.jmxremote=true activate the remote JMX (Java Management eXtensions) endpoint
  • -Dcom.sun.management.jmxremote.port=8849 tells the port we are going to use for remote JMX
  • -Dcom.sun.management.jmxremote.local.only=false yep, this is just for local profiling but without this we won’t be able to connect to the container (remember, it is “like” running in a separate machine)
  • -Dcom.sun.management.jmxremote.authenticate=false of course we don’t want to authenticate, this is for our local profiling!
  • -Dcom.sun.management.jmxremote.ssl=false no SSL, again, this makes sense when you are profiling a docker app in your machine
  • -Djava.rmi.server.hostname=localhost we are setting the Java RMI (Remote Method Invocation) server to localhost
  • -Dcom.sun.management.jmxremote.rmi.port=8849 again, the same port for JMX is used for RMI

Of course we need to expose the port 8849 as well to make all of this work, to simplify running the application I simply use a docker-compose.yml file instead of writing a lot in the command line all the time for profiling:

version: '3'
services:
    app:
        build: .
        ports:
            - 8849:8849
        entrypoint:
            - java
            - "-jar"
            - "-Dcom.sun.management.jmxremote=true"
            - "-Dcom.sun.management.jmxremote.port=8849"                        
            - "-Dcom.sun.management.jmxremote.ssl=false"
            - "-Dcom.sun.management.local.only=false"
            - "-Dcom.sun.management.authenticate=false"
            - "-Djava.rmi.server.hostname=localhost"
            - "-Djcom.sun.management.jmxremote.rmi.port=8849"
            - app.jar

Of course, feel free to add as many other parameters you need, but this is the minimum I needed to do the local profiling inside a container.

Now it is time to run the container or orchestration and start profiling, run the orchestration with docker-compose up -d in the directory where your docker-compose.yml file is and then start VisualVM and select “Add JMX host…” in the main menu (or the weird third icon in the UI):

VisualVM

Done! now you can go crazy and apply your stress test to your application and see how it goes, it is basically like profiling locally but using a container, or in my case, a whole orchestration. Happy profiling!