Skip to content

Commit 62c5ec5

Browse files
committed
Add Memory Leak Sample Application
1 parent 2bb8aa3 commit 62c5ec5

File tree

8 files changed

+275
-0
lines changed

8 files changed

+275
-0
lines changed

memoryleak/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Sample to a memory leak in Java Application
2+
===========================================
3+
4+
This sample application implements a simple caching solution using a `HashMap`.
5+
6+
For example, see following code.
7+
8+
Map<Key, String> map = new HashMap<>();
9+
while (true) {
10+
for (int i = 0; i < max; i++) {
11+
Key key = new Key(i);
12+
if (!map.containsKey(key)) {
13+
map.put(key, "Number:" + i);
14+
}
15+
}
16+
}
17+
18+
The map is not expected to grow beyond `max` value as all keys will be there in the map after the first `while` loop.
19+
20+
However, if the `Key` class does not contain a proper `equals()` implementation along with the `hashCode()`
21+
implementation, the key elements will be keep getting added as the `!map.containsKey(key)` will always return `false`.
22+
This is a good example of a memory leak and this sample was inspired by the blog post:
23+
"[How to create a memory leak](https://plumbr.io/blog/memory-leaks/how-to-create-a-memory-leak)"
24+
25+
### How to run
26+
27+
The application will throw Out of Memory error after some time when you run following command
28+
29+
`java -Xms1g -Xmx1g -XX:+PrintGCDetails -XX:+PrintGC -XX:+PrintGCDateStamps -Xloggc:gc.log
30+
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="/tmp/heap-dump.hprof"
31+
-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
32+
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
33+
-XX:StartFlightRecording=settings=profile,duration=5m,name=MemoryLeak,filename=memoryleak.jfr
34+
-XX:FlightRecorderOptions=loglevel=info
35+
-jar target/memoryleak.jar`
36+
37+
### Analyzing Java Flight Recording
38+
39+
In Memory -> Memory tab, you should see a high memory growth rate.
40+
41+
Also see Memory -> Garbage Collections tab and check the frequency of GC events.
42+
43+
### Analyzing Garbage Collection Logs (GC)
44+
45+
You can analyze the GC logs using [GCViewer](https://github.com/chewiebug/GCViewer)
46+
47+
### How to fix the Memory Leak
48+
49+
Run the application with `--key-type GOOD` parameter, which will use a Key object with proper `equals()` implementation.

memoryleak/pom.xml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
# Copyright 2018 M. Isuru Tharanga Chrishantha Perera
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
20+
<parent>
21+
<groupId>com.github.chrishantha.sample</groupId>
22+
<artifactId>java-samples</artifactId>
23+
<version>0.0.2-SNAPSHOT</version>
24+
<relativePath>../pom.xml</relativePath>
25+
</parent>
26+
27+
<modelVersion>4.0.0</modelVersion>
28+
<artifactId>memoryleak</artifactId>
29+
<packaging>jar</packaging>
30+
<name>memoryleak</name>
31+
32+
<dependencies>
33+
<dependency>
34+
<groupId>com.github.chrishantha.sample</groupId>
35+
<artifactId>base</artifactId>
36+
</dependency>
37+
</dependencies>
38+
39+
<build>
40+
<plugins>
41+
<plugin>
42+
<groupId>org.apache.maven.plugins</groupId>
43+
<artifactId>maven-shade-plugin</artifactId>
44+
</plugin>
45+
</plugins>
46+
</build>
47+
</project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2018 M. Isuru Tharanga Chrishantha Perera
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.chrishantha.sample.memoryleak;
17+
18+
public class BadKey extends Key {
19+
20+
public BadKey(long NUMBER, long LENGTH) {
21+
super(NUMBER, LENGTH);
22+
}
23+
24+
@Override
25+
public boolean equals(Object o) {
26+
return super.equals(o);
27+
}
28+
29+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2018 M. Isuru Tharanga Chrishantha Perera
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.chrishantha.sample.memoryleak;
17+
18+
import java.util.Objects;
19+
20+
public class GoodKey extends Key {
21+
22+
public GoodKey(long NUMBER, long LENGTH) {
23+
super(NUMBER, LENGTH);
24+
}
25+
26+
@Override
27+
public boolean equals(Object o) {
28+
if (this == o) return true;
29+
if (o == null || getClass() != o.getClass()) return false;
30+
Key key = (Key) o;
31+
return Objects.equals(KEY, key.KEY);
32+
}
33+
34+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2018 M. Isuru Tharanga Chrishantha Perera
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.chrishantha.sample.memoryleak;
17+
18+
import java.util.Objects;
19+
20+
public abstract class Key {
21+
22+
protected final String KEY;
23+
24+
public Key(final long NUMBER, final long LENGTH) {
25+
String binaryString = Long.toBinaryString(NUMBER);
26+
StringBuilder keyBuilder = new StringBuilder();
27+
long limit = LENGTH - binaryString.length();
28+
for (long i = 0; i < limit; i++) {
29+
keyBuilder.append('0');
30+
}
31+
keyBuilder.append(binaryString);
32+
KEY = keyBuilder.toString();
33+
}
34+
35+
@Override
36+
public final int hashCode() {
37+
return Objects.hash(KEY);
38+
}
39+
40+
@Override
41+
public final String toString() {
42+
StringBuilder builder = new StringBuilder();
43+
builder.append(this.getClass().getSimpleName());
44+
builder.append(" [key=");
45+
builder.append(KEY);
46+
builder.append("]");
47+
return builder.toString();
48+
}
49+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2018 M. Isuru Tharanga Chrishantha Perera
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.chrishantha.sample.memoryleak;
17+
18+
import com.beust.jcommander.Parameter;
19+
import com.github.chrishantha.sample.base.SampleApplication;
20+
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
public class MemoryLeakApplication implements SampleApplication {
25+
26+
private enum KeyType {
27+
BAD,
28+
GOOD
29+
}
30+
31+
@Parameter(names = "--length", description = "Length of the Key")
32+
private long length = 1024 * 1024;
33+
34+
@Parameter(names = "--max", description = "Maximum limit to generate keys")
35+
private int max = 100;
36+
37+
@Parameter(names = "--key-type", description = "Key Type: Good or Bad")
38+
private KeyType keyType = KeyType.BAD;
39+
40+
@Override
41+
public void start() {
42+
Map<Key, String> map = new HashMap<>();
43+
while (true) {
44+
for (int i = 0; i < max; i++) {
45+
Key key = KeyType.GOOD.equals(keyType) ? new GoodKey(i, length) : new BadKey(i, length);
46+
if (!map.containsKey(key)) {
47+
map.put(key, "Number:" + i);
48+
}
49+
}
50+
}
51+
}
52+
53+
@Override
54+
public String toString() {
55+
StringBuilder builder = new StringBuilder();
56+
builder.append("MemoryLeakApplication [length=");
57+
builder.append(length);
58+
builder.append(", max=");
59+
builder.append(max);
60+
builder.append(", keyType=");
61+
builder.append(keyType);
62+
builder.append("]");
63+
return builder.toString();
64+
}
65+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
com.github.chrishantha.sample.memoryleak.MemoryLeakApplication

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<module>hotmethods</module>
4949
<module>latencies</module>
5050
<module>allocations</module>
51+
<module>memoryleak</module>
5152
</modules>
5253

5354
<dependencyManagement>

0 commit comments

Comments
 (0)