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