VisualVM and jconsole are two useful tools for debugging JVM issues. However they both rely on a JMX port to be open on the remote instance. You can work around this on the fly by running jstatd
on the remote host, but you’ll find certain things disabled. So ideally, the jmx port will be enabled on startup.
This can be accomplished by doing something like this:
$ java -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=1080 \ -Dcom.sun.management.jmxremote.local.only=false \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false \ -jar MyApp.jar
Then connecting via jmx to port 1080 on the remote host.
Automated deployments
The problem with this approach becomes quickly apparent in an automated environment. If two applications are deployed on the same machine and they both try to open a 1080 port, one of them will fail. This conflict can also arise from non-obvious services, like running an ActiveMQ broker which by default opens a 1080 port.
The obvious next step is to try and assign a unique jmx port to each instance by hand. This is doable, but can quickly become a mess unless you develop some sort of jmx port registry system.
Fortunately there is a better way.
Ephemeral ports
Hotspot and openjdk have had an undocumented feature where setting com.sun.management.jmxremote.port=0
will cause the jvm to ask the OS to assign an ephemeral port. This will guarantee an unused port.
This has been around for years, so I don’t expect it to change. But being undocumented, you never know.
What port?
If you’re familiar with the usual unix tools, you should know that you can use something like lsof
to figure out the port being used:
$ jps -l # figure out pid $ lsof -p <pid> -n -i
Another approach that is more script / human friendly is to enable logging of the jmx server component. Once enabled it will print out the jmx port on startup to standard out. This can be done with a simple java.util.logging.config.file:
# logging.properties handlers= java.util.logging.ConsoleHandler .level= INFO java.util.logging.ConsoleHandler.level = CONFIG java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter sun.management.jmxremote.level = CONFIG
Then modify the command line to specify the properties file and set the jmx port to 0:
$ java -Djava.util.logging.config.file=logging.properties -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=0 \ -Dcom.sun.management.jmxremote.local.only=false \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false \ -jar MyApp.jar
You should see the jmx port printed at startup as a CONFIG message:
CONFIG: JMX Connector ready at: service:jmx:rmi:///jndi/rmi://disaster:39148/jmxrmi
Here the process got assigned 39148. This can be further processed by scripts to display the port on a webpage for quick access.
Very helpful, thx
LikeLike