JVM remote debugging lets you attach a debugger to a running Java process, even on a production server, without stopping or restarting it.
Here’s a live example. Imagine you have a Java application running in a Docker container.
# On the server where your Java app is running
docker exec -it your_java_app_container bash
# Inside the container, find your Java process ID (PID)
ps aux | grep java
# Let's say the PID is 12345
Now, on your local development machine, you’ll run your IDE (like IntelliJ IDEA or Eclipse) with a remote debugging configuration. In IntelliJ, you’d go to Run -> Edit Configurations..., click +, and select Remote JVM Debug.
Here’s a typical configuration:
- Host:
your_server_ip_address(orlocalhostif debugging locally) - Port:
5005(this is the default, but can be anything) - Command line arguments for the JVM: This is the crucial part. When you start your Java application on the server, you need to include these JVM arguments:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005-agentlib:jdwp: Tells the JVM to load the Java Debug Wire Protocol (JDWP) agent.transport=dt_socket: Specifies that the debugger will communicate over a socket.server=y: Makes the JVM act as a debug server, waiting for a debugger to attach.suspend=n: The JVM will not wait for the debugger to attach before starting the application. If you set this toy, your application will pause at startup until you connect.address=*:5005: The JVM will listen for debugger connections on all network interfaces (*) on port5005.
So, if you were starting your Java app using a command like java -jar myapp.jar, you’d change it to:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar myapp.jar
Once your Java application is running with these arguments, you can click the "Debug" button in your IDE’s remote debugging configuration. Your IDE will attempt to connect to your_server_ip_address:5005. If successful, you’ll see a message like "Connected to the target VM, address: 'your_server_ip_address:5005', transport: 'socket'".
Now, you can set breakpoints in your local IDE, step through code, inspect variables, and evaluate expressions in the live running application on the server, just as if it were running locally. This is incredibly powerful for diagnosing issues that only occur in specific production environments or under heavy load, without the risk of downtime.
The core problem this solves is the inability to easily inspect the state of a long-running or production Java process. Traditionally, debugging required attaching a debugger before the application started (suspend=y) or relying on logs, which can be insufficient for complex, intermittent bugs. Remote debugging bypasses this by allowing a "hot attach" to an already running JVM.
The address parameter is often a point of confusion. Using address=5005 will bind to the default loopback interface (localhost) on the server. If you’re debugging from a different machine, you must use address=*:5005 or address=your_server_specific_ip:5005 to ensure the JVM listens on an interface accessible from your development machine. If you’re behind a firewall or NAT, you might need to configure port forwarding on your server or use an SSH tunnel.
A common pitfall is forgetting to restart the Java application after adding the -agentlib arguments. The JVM only reads these arguments at startup. If the application is already running without them, you’ll need to restart it with the arguments in place for remote debugging to work.
Another advanced technique is using suspend=y with an SSH tunnel. You can establish an SSH connection to your server, forward the debugging port locally, and then attach your IDE to localhost:5005. This is much more secure than exposing the debug port directly to the internet.
The next hurdle you’ll likely face is understanding how to secure this connection, as exposing a debug port can be a significant security risk.