Memory overhead of having too many contexts
Tomáš Zíma
tomas at tzima.cz
Sun Feb 12 06:26:51 UTC 2017
Hello.
For one project, I need to be able to evaluate a lot of scripts
independently. However, after implementing a solution where each script
uses a different ScriptContext, I found out there's a huge memory overhead.
I reproduced it in the following code:
NashornScriptEngine engine = (NashornScriptEngine) new
NashornScriptEngineFactory().getScriptEngine();
CompiledScript compiledFunction = engine.compile("function f() {}");
List<ScriptContext> contexts = new ArrayList<>();
for (int i = 0; i < 10_000; i++) {
SimpleScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(),
ScriptContext.ENGINE_SCOPE);
compiledFunction.eval(context);
contexts.add(context);
}
System.gc();
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
System.out.printf("Heap: %,12d B\n",
memoryMXBean.getHeapMemoryUsage().getUsed());
System.out.printf("Non-Heap: %,12d B\n",
memoryMXBean.getNonHeapMemoryUsage().getUsed());
Thread.sleep(Long.MAX_VALUE);
Running on my machine with OpenJDK 1.8.0_111, it prints the following:
Heap: 1,093,960,464 B
Non-Heap: 22,980,864 B
I looked into the heap dump in VisualVM and found out, to my surprise,
that there are about 2.2 million instances of
jdk.nashorn.internal.runtime.CompiledFunction. These instances occupy 11
% of heap in size. There's also 1.3 million instances of
jdk.nashorn.internal.runtime.ScriptFunction (9 % of heap in size).
It scales with the amount of contexts. For example, when I create only
1000 contexts, there's about 1/10th of instances.
Why is there so many? I wonder if creating a new context doesn't result
in recompiling/reinterpreting/redefining all standard functions which
are normally present in global namespace. That could be an answer for
why there's about ~350 functions (either CompiledFunction or
ScriptFunction) per context. Obviously, this is just a guess since I
don't know much about how Nashorn works internally.
Note: When using Bindings instead of ScriptContext, it ends up using
about twice as small heap but creates the same amount of instances of
CompiledFunction and ScriptFunction.
What can I do to prevent such tremendous memory costs? We previously
used Rhino with the same amount of independent contexts and it certainly
had much smaller memory footprint.
Thanks.
-- tzima
More information about the nashorn-dev
mailing list