Profiling Remote Java Application

29 September 2017

jvisualvm is a tool that provides a visual interface for viewing detailed information about Java technology-based applications while they are running. You can his to view data related to your local applications and those running on remote hosts. You can also capture data about JVM software instances and save the data to your local system.

There are couple of ways to use it, one is using jstatd and the other is by JMX (Java management Extensions). I tried jstatd first but didn't work. The later worked on my CentOS machine using the visualvm I downloaded from visualvm.github.io.

By using jstatd

Grant jstatd full security permission

Create a file called jstatd.all.policy. If in production, make sure your server port are locked down to your local IP.

grant codebase "file:/opt/java8/lib/tools.jar" {
    permission java.security.AllPermission;
};

Please use the full-path of your JDK's tools.jar. The above policy should avoid you from getting this access denied message

Could not create remote object
access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses" "write")
java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses" "write")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.System.setProperty(System.java:792)
        at sun.tools.jstatd.Jstatd.main(Jstatd.java:139)

Run jstatd

jstatd -J-Djava.security.policy=/home/user/jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.1.1 -J-Djava.net.preferIPv4Stack=true -J-Djava.rmi.server.logCalls=true

Replace /home/user/jstatd.all.policy by your actual file and 192.168.1.1 by the remote IP of your target machine.

By using JMX

Add some JVM arguments

-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.rmi.port=1098
-Djava.rmi.server.hostname=localhost

If running CentOS, run this to open up port 1098.

sudo firewall-cmd --zone=public --add-port=1098/tcp

Launch SSH with local port forwarding

ssh -v -L 1098:localhost:1098 user@my.remote.server.com

Replace user by your login and my.remote.server.com by your own hostname or IP address. Since I'm running SSH in verbose mode, you should see debug messages like below.

debug1: channel 6: free: direct-tcpip: listening port 1098 for localhost port 1098, connect from 127.0.0.1 port 8077 to 127.0.0.1 port 9001, nchannels 9
debug1: channel 8: free: direct-tcpip: listening port 1098 for localhost port 1098, connect from 127.0.0.1 port 8078 to 127.0.0.1 port 9001, nchannels 8
debug1: channel 4: free: direct-tcpip: listening port 1098 for localhost port 1098, connect from 127.0.0.1 port 8073 to 127.0.0.1 port 9001, nchannels 7

Connect VisualVM to Local

Right-click on Local -> Add JMX Connection. On connection type localhost:1098

Alternative to VisualVM

Run jconsole. On Remote Process, type localhost:1098