Troubleshooting Java Memory Leakage
Memory leakage in Java occurs when a program unintentionally retains references to no longer needed objects, preventing the Java Garbage…
Troubleshooting Java Memory Leakage
Memory leakage in Java occurs when a program unintentionally retains references to no longer needed objects, preventing the Java Garbage Collector (GC) from reclaiming memory. This can lead to the following symptoms:
- Increased memory consumption.
- Performance degradation.
- Application to crash due to OutOfMemoryException.
This post will cover the following to improve our diagnosis and remediate the problem:
- Trivial application with an embedded memory leakage problem.
- Tools to help conduct memory leakage analysis.
- Remediate the problem.
Trivial Application with an Embedded Memory Leakage Problem
The application will use a HashSet to produce a memory leakage problem.
In Java, a HashSet is a part of the Java Collections Framework and implements the Set interface. It stores a collection of unique elements, not allowing duplicate values. HashSet uses the .equals() method to check for duplicates.
Armed with this information, class Foo is created without overriding .equals() . In theory, the HashSet is unable to distinguish Foo instances with the same value. Therefore adding this to the HashSet will result in the size growth thus producing a memory leakage.
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;
public class MemLeakApplication {
static class Foo {
private String value;
public Foo(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
public static void main(String[] args) {
Set<Foo> set = new HashSet<>();
IntStream.range(0, Integer.MAX_VALUE)
.forEach(it -> {
Foo foo = new Foo("KEY");
set.add(foo);
System.out.println("Set size: " + set.size());
});
}
}
Tools to Help Conduct Memory Leakage Analysis
This post will cover several options to conduct the analysis:
- jcmd
- Visual VM
JCMD
jcmd is a command-line tool included in the Java Development Kit (JDK) for sending diagnostic commands to Java applications on the Java Virtual Machine (JVM). It helps troubleshoot performance issues, manages Java applications through various commands (like garbage collection and thread dumps), and collects performance metrics for monitoring and tuning.
# Identify the process ID (PID) of the Java Application i.e. MemLeakApplication.
> jcmd
1537 Eclipse
4679 MemLeakApplication
# Perform a heap dump
> jcmd 4679 GC.heapdump memleak.hprof
# Get a histogram of object types in the Java heap of a running Java application
> jcmd 4679 GC.class_histogram > memleak.histo
The histogram has identified a huge number of Foo instances, which is expected due to the missing .equals() .
> head -n 20 memleak.histo
4679:
num #instances #bytes class name (module)
-------------------------------------------------------
1: 38991377 1247724064 java.util.HashMap$Node (java.base@18.0.2.1)
2: 38990411 623846576 MemLeakApplication$Foo
3: 258 268459424 [Ljava.util.HashMap$Node; (java.base@18.0.2.1)
4: 6897 323232 [B (java.base@18.0.2.1)
5: 1429 176072 java.lang.Class (java.base@18.0.2.1)
6: 6813 163512 java.lang.String (java.base@18.0.2.1)
7: 913 89176 [Ljava.lang.Object; (java.base@18.0.2.1)
8: 7 33032 [C (java.base@18.0.2.1)
9: 480 31944 [I (java.base@18.0.2.1)
10: 982 31424 java.util.concurrent.ConcurrentHashMap$Node (java.base@18.0.2.1)
11: 22 23584 [Ljava.util.concurrent.ConcurrentHashMap$Node; (java.base@18.0.2.1)
12: 259 12432 java.util.HashMap (java.base@18.0.2.1)
13: 341 10912 jdk.internal.math.FDBigInteger (java.base@18.0.2.1)
14: 362 8688 java.lang.module.ModuleDescriptor$Exports (java.base@18.0.2.1)
15: 256 6144 java.lang.Long (java.base@18.0.2.1)
16: 249 5976 java.util.ImmutableCollections$Set12 (java.base@18.0.2.1)
17: 62 4960 java.net.URI (java.base@18.0.2.1)
Unfortunately, I could not find a tool in JDK to analyze the heap dump hence resorting to Visual VM. Kindly leave a comment if there is such a tool. Meanwhile the following is a screen capture from Visual VM after loading the .hprof file.

The analysis is the same as the histogram hence using the JDK solution should suffice most cases.
Visual VM
VisualVM is a visual tool that provides detailed information about Java applications while they are running on the Java Virtual Machine (JVM). It is primarily used for monitoring, troubleshooting, and profiling Java applications.
Here are some screen captures showing the metrics across a short period.

In particular, the heap space is growing due to the growing number Foo instances, whereas the stack memory remains near constant.

Metaspace is an area of memory used by the Java Virtual Machine (JVM) to store class metadata. Understanding Metaspace is crucial for efficiently managing memory, especially when large numbers of classes or dynamic class loading occur.
The memory histogram aligns with jcmd , both indicating the large number of Foo instances.

Remediate the problem
To resolve the memory leakage, the .hashcode() and .equals() is overridden as follows:
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.IntStream;
public class MemLeakApplication {
static class Foo {
private String value;
public Foo(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Foo other = (Foo) obj;
return Objects.equals(value, other.value);
}
}
public static void main(String[] args) {
Set<Foo> set = new HashSet<>();
IntStream.range(0, Integer.MAX_VALUE)
.forEach(it -> {
Foo foo = new Foo("KEY");
set.add(foo);
System.out.println("Set size: " + set.size());
});
}
}
Running the application again and conducting the analysis using JCMD yields the following results:

The heap memory did not ramp up as before, which indicates the HashSet was able to identify duplicate Foo instance after overriding the .equals().

The histogram further proves the number of Foo instances has decreased and garbage collection is successful.
Summary
In this post, both JCMD and Visual VM were able to pinpoint where the memory leakage was. In most cases the JCMD is more convenient since it is rolled out with the JDK and therefore easily accessible. Visual VM has an intuitive interface and conveys the same data as JCMD but with time series metrics. Visual VM may be unavailable due to the working environment such as Internet proxies.
메타데이터
- post_id
- edccef06d41d
- slug
- troubleshooting-java-memory-leakage-edccef06d41d
- url
- https://towardsdev.com/troubleshooting-java-memory-leakage-edccef06d41d
- canonical_url
- https://towardsdev.com/troubleshooting-java-memory-leakage-edccef06d41d
- author_url
- https://medium.com/@dennisholee
- status
- ok
- fetched_at
- 2026-06-17 15:37:45