Edit: synopsis now reflects true cause of the leak. Below is the original report.
FULL PRODUCT VERSION :
build 1.8.0_172
ADDITIONAL OS VERSION INFORMATION :
macOS Sierra Version 10.12
CentOS 7
EXTRA RELEVANT SYSTEM CONFIGURATION :
-XX:NativeMemoryTracking=detail
A DESCRIPTION OF THE PROBLEM :
Calling an interrupt() may cause Internal
Slow Internal memory leak when running following code:
import java.util.ArrayList;
public class Main {
public static class LockingRunnable implements Runnable {
@Override
public void run() {
Object object = new Object();
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws Exception {
LockingRunnable lockingRunnable = new LockingRunnable();
new Thread(lockingRunnable).start();
ArrayList<Thread> arrayList = new ArrayList(100);
while (true) {
Object lock = new Object();
synchronized (lock) {
lock.wait(50);
lockingRunnable = new LockingRunnable();
Thread thread = new Thread(lockingRunnable);
arrayList.add(thread);
thread.start();
if (arrayList.size() > 100) {
arrayList.stream().forEach(item->item.interrupt());
arrayList.clear();
}
}
}
}
}
The behavior is exactly as described in this javadoc section:
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#interrupt--
everything works as expected but Internal memory grows slowly over time
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the program attached for several minutes with NMT enabled (-XX:NativeMemoryTracking=detail)
when program starts run following:
jps | grep \ Main | awk '{system("jcmd "$1" VM.native_memory baseline")}'
after that create shell executable containing following lines:
while :
do
jps | grep \ Main | awk '{system("jcmd "$1" VM.native_memory detail.diff")}' | grep Internal\ \(
sleep 5
done
- Internal (reserved=10760KB +53KB, committed=10760KB +53KB)
- Internal (reserved=10788KB +81KB, committed=10788KB +81KB)
- Internal (reserved=10816KB +109KB, committed=10816KB +109KB)
- Internal (reserved=10841KB +134KB, committed=10841KB +134KB)
- Internal (reserved=10845KB +138KB, committed=10845KB +138KB)
....
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No internal memory leak
ACTUAL -
Internal memory leak
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.ArrayList;
public class Main {
public static class LockingRunnable implements Runnable {
@Override
public void run() {
Object object = new Object();
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws Exception {
LockingRunnable lockingRunnable = new LockingRunnable();
new Thread(lockingRunnable).start();
ArrayList<Thread> arrayList = new ArrayList(100);
while (true) {
Object lock = new Object();
synchronized (lock) {
lock.wait(50);
lockingRunnable = new LockingRunnable();
Thread thread = new Thread(lockingRunnable);
arrayList.add(thread);
thread.start();
if (arrayList.size() > 100) {
arrayList.stream().forEach(item->item.interrupt());
arrayList.clear();
}
}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
This is mostly to demonstrate how you should NOT write the code.
I found similar code in our old projects. Probably you should use thread pool
and java.util.concurrent stuff to avoid the issue.
FULL PRODUCT VERSION :
build 1.8.0_172
ADDITIONAL OS VERSION INFORMATION :
macOS Sierra Version 10.12
CentOS 7
EXTRA RELEVANT SYSTEM CONFIGURATION :
-XX:NativeMemoryTracking=detail
A DESCRIPTION OF THE PROBLEM :
Calling an interrupt() may cause Internal
Slow Internal memory leak when running following code:
import java.util.ArrayList;
public class Main {
public static class LockingRunnable implements Runnable {
@Override
public void run() {
Object object = new Object();
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws Exception {
LockingRunnable lockingRunnable = new LockingRunnable();
new Thread(lockingRunnable).start();
ArrayList<Thread> arrayList = new ArrayList(100);
while (true) {
Object lock = new Object();
synchronized (lock) {
lock.wait(50);
lockingRunnable = new LockingRunnable();
Thread thread = new Thread(lockingRunnable);
arrayList.add(thread);
thread.start();
if (arrayList.size() > 100) {
arrayList.stream().forEach(item->item.interrupt());
arrayList.clear();
}
}
}
}
}
The behavior is exactly as described in this javadoc section:
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#interrupt--
everything works as expected but Internal memory grows slowly over time
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the program attached for several minutes with NMT enabled (-XX:NativeMemoryTracking=detail)
when program starts run following:
jps | grep \ Main | awk '{system("jcmd "$1" VM.native_memory baseline")}'
after that create shell executable containing following lines:
while :
do
jps | grep \ Main | awk '{system("jcmd "$1" VM.native_memory detail.diff")}' | grep Internal\ \(
sleep 5
done
- Internal (reserved=10760KB +53KB, committed=10760KB +53KB)
- Internal (reserved=10788KB +81KB, committed=10788KB +81KB)
- Internal (reserved=10816KB +109KB, committed=10816KB +109KB)
- Internal (reserved=10841KB +134KB, committed=10841KB +134KB)
- Internal (reserved=10845KB +138KB, committed=10845KB +138KB)
....
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No internal memory leak
ACTUAL -
Internal memory leak
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.util.ArrayList;
public class Main {
public static class LockingRunnable implements Runnable {
@Override
public void run() {
Object object = new Object();
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) throws Exception {
LockingRunnable lockingRunnable = new LockingRunnable();
new Thread(lockingRunnable).start();
ArrayList<Thread> arrayList = new ArrayList(100);
while (true) {
Object lock = new Object();
synchronized (lock) {
lock.wait(50);
lockingRunnable = new LockingRunnable();
Thread thread = new Thread(lockingRunnable);
arrayList.add(thread);
thread.start();
if (arrayList.size() > 100) {
arrayList.stream().forEach(item->item.interrupt());
arrayList.clear();
}
}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
This is mostly to demonstrate how you should NOT write the code.
I found similar code in our old projects. Probably you should use thread pool
and java.util.concurrent stuff to avoid the issue.