Mastering JVisualVM: Detect Memory Leaks and Monitor Java Applications
JVisualVM, bundled with JDK, offers a visual interface to monitor JVM threads, memory, CPU usage, and perform heap dumps, enabling developers to detect memory leaks, analyze object references, and remotely monitor applications such as Tomcat, with step-by-step installation, plugin usage, and code examples.
JVisualVM Overview
VisualVM is a profiling sub‑project of NetBeans that ships with JDK 6 update 7 and later. It can monitor threads, memory usage, CPU time per method, and objects that have been garbage‑collected. The executable jvisualvm.exe resides in the JDK bin directory.
VisualVM provides a graphical UI to view detailed information about Java applications running on a JVM, both locally and remotely. It organizes data retrieved by JDK tools and allows you to capture and save JVM data for later analysis or sharing.
After launching jvisualvm.exe, you can select local or remote JVMs similar to jconsole. The main interface looks like this:
VisualVM’s functionality is extended via plugins. Some plugins focus on GC monitoring, others on memory or thread analysis.
How to install a plugin:
1. Choose Tools > Plugins from the main menu. 2. In the Available Plugins tab, check the plugin you want to install. 3. Click Install and follow the wizard.
Example screenshot from Eclipse (process ID 22296) shows the system and JVM sections, with JVM parameters and system properties available.
The most frequently used plugins are:
Monitor (CPU, memory, classes, threads charts)
Threads (similar to jconsole)
Visual GC (shows young/old generation memory changes, GC frequency, and pause times)
VisualVM can also capture heap dumps for offline analysis.
Case Study: Simulating a Memory Leak
Steps to create a leak example:
Define a static HashMap to hold objects.
Loop to create many TestMemory instances and put them into the map.
Configure JVM parameters:
-Xms512m
-Xmx512m
-XX:-UseGCOverheadLimit
-XX:MaxPermSize=50mRun the program and start VisualVM monitoring.
Analyzing the Memory Leak with JVisualVM
Open the Visual GC tab and observe the heap usage after each printed checkpoint ("first", "second", "third", "forth"). Screenshots show that the old generation continues to be GC'd without releasing memory, indicating a leak.
Compare two heap dumps (taken after "first" and "forth"). The comparison reveals that TestMemory instances keep increasing, confirming the leak.
To see object references, right‑click the TestMemory class and choose “Show in Instances View”. The view shows the total number of instances, their structure, and that they are referenced from CyclicDependencies via the static HashMap.
Thus the leak location is identified and can be addressed.
Full Sample Code
import java.util.HashMap;
import java.util.Map;
public class CyclicDependencies {
// Cache object
private static final Map<String, TestMemory> map = new HashMap<>();
public static void main(String[] args) {
try {
Thread.sleep(10000); // give VisualVM time to start
} catch (InterruptedException e) {
e.printStackTrace();
}
// Add objects to cache
for (int i = 0; i < 1_000_000; i++) {
TestMemory t = new TestMemory();
map.put("key" + i, t);
}
System.out.println("first");
// Pause for heap dump
try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
for (int i = 0; i < 1_000_000; i++) {
TestMemory t = new TestMemory();
map.put("key" + i, t);
}
System.out.println("second");
try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
for (int i = 0; i < 3_000_000; i++) {
TestMemory t = new TestMemory();
map.put("key" + i, t);
}
System.out.println("third");
try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
for (int i = 0; i < 4_000_000; i++) {
TestMemory t = new TestMemory();
map.put("key" + i, t);
}
System.out.println("forth");
try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("qqqq");
}
}Remote Monitoring of Tomcat with JVisualVM
Configure the remote Tomcat’s catalina.sh to expose JMX:
JAVA_OPTS="$JAVA_OPTS \
-Djava.rmi.server.hostname=192.168.122.128 \
-Dcom.sun.management.jmxremote.port=18999 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false"Then in VisualVM:
Right‑click Remote and choose Add Remote Host , entering the server IP.
Right‑click the new host, select Add JMX Connection , and provide the configured port.
Double‑click the connection to start monitoring.
References
CSDN article
CNBlogs tutorial
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
