FULL PRODUCT VERSION :
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
A DESCRIPTION OF THE PROBLEM :
There is a memory leak in nashorn which will cause gc slowdown and OutOfMemoryErrors, but heap dumps will not report relevant gc roots.
In production this means applications will run out of memory even though they use the right algorithms and the heap dump won't really help the developer to figure out whats going on.
This is already reported as fixed in openjdk 11. Since this looks like a severe issue could that fix please be backported to a released version ?
https://bugs.openjdk.java.net/browse/JDK-8173594
OpenJDK mailing list; last year
http://mail.openjdk.java.net/pipermail/hotspot-gc-use/2017-February/002632.html
THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Yes
THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run Attached code using -Xmx2g
EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected:
No OutOfMemoryError gets thrown.
Expected otherwise: If an Out of memory Error does occur, the heap dump should have information on what objects are leaking from which root.
Actual:
if you run the code using 2Gb of Heap you will get an OOME.
If you use 4gb of Heap you will have 2 LeakImpl Objects, which have no reported path to a valid gc root, yet the gc will refuse to collect these objects
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at frei.demo.LeakDemo$LeakImpl.<init>(LeakDemo.java:52)
at frei.demo.LeakDemo.simulateLoad(LeakDemo.java:27)
at frei.demo.LeakDemo.main(LeakDemo.java:19)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.CompiledScript;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.util.function.Function;
public final class LeakDemo {
private static NashornScriptEngine ENGINE = getNashornScriptEngine();
private static CompiledScript SCRIPT;
public static void main(String[] args) throws Exception {
simulateLoad();
simulateLoad();
System.gc();
Thread.sleep(1000000);
}
private static void simulateLoad() throws ScriptException {
final CompiledScript compiledScript = getCompiledScript(ENGINE);
compiledScript.eval(new SimplestBindings(new LeakImpl()));
}
private static NashornScriptEngine getNashornScriptEngine() {
final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
final NashornScriptEngine scriptEngine = (NashornScriptEngine) factory.getScriptEngine();
return scriptEngine;
}
private static CompiledScript getCompiledScript(final NashornScriptEngine scriptEngine) throws ScriptException {
if (SCRIPT == null) {
SCRIPT = scriptEngine.compile(" var pivot = getItem(\"pivot\");");
}
return SCRIPT;
}
public interface Leak {
LiveItem getItem(String id);
}
public static final class LeakImpl implements Leak {
private final byte[] payload = new byte[1024 * 1024 * 1024];
@Override
public LiveItem getItem(final String id) {
return new LiveItem() {
};
}
}
public interface LiveItem {
}
public static final class SimplestBindings extends SimpleBindings {
public SimplestBindings(Leak leak) {
put("getItem",(Function< String, LiveItem>) leak::getItem);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
aggressively null out as much as you can and hope for the best.