Skip to content

When logging exceptions, the exception stack traces include dynamically generated class names, which leads to a memory leak. #3282

@Tonys-L

Description

@Tonys-L

Description

When using the Nashorn JavaScript engine, if an exception is thrown internally within the JavaScript code and this exception is logged using a logger, it results in a memory leak. Tracing the log4j source code reveals that log4j uses ThrowableProxy when handling exception parameters. ThrowableProxy calls loadClass. Because Nashorn dynamically generates some classes, the exception stack trace includes dynamically generated class names, which are not cached. This causes loadClass to add these class names to the parallelLockMap inside the ClassLoader, leading to a memory leak.

Configuration

Version: 2.24.1

Operating system: Windows10

JDK: 1.8.0_331

Logs

Below are some log fragments. Note the class names in the exception stack trace that follow the pattern jdk.nashorn.internal.scripts.Script$Recompilation$19663$15$^eval_.test. The numbers following Recompilation$ are incrementing.

a 2024-12-06 20:11:38,104 [main] ERROR c.l.t.l.TestJsEngine - Script execution exception
javax.script.ScriptException: Error: error in <eval> at line number 1 at column number 16
	at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:392) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190) ~[nashorn.jar:?]
	at com.lt.test.log4j.TestJsEngine.main(TestJsEngine.java:33) [classes/:?]
Caused by: jdk.nashorn.internal.runtime.ECMAException: Error: error
	at jdk.nashorn.internal.objects.NativeError.initException(NativeError.java:137) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:102) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:106) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:110) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.constructor(NativeError.java:129) ~[nashorn.jar:?]
	at jdk.nashorn.internal.scripts.Script$Recompilation$19663$15$\^eval\_.test(<eval>:1) ~[?:?]
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637) ~[nashorn.jar:?]
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494) ~[nashorn.jar:?]
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:386) ~[nashorn.jar:?]
	... 2 more
aaaaa 2024-12-06 20:11:40,699 [main] ERROR c.l.t.l.TestJsEngine - Script execution exception
javax.script.ScriptException: Error: error in <eval> at line number 1 at column number 16
	at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:392) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190) ~[nashorn.jar:?]
	at com.lt.test.log4j.TestJsEngine.main(TestJsEngine.java:33) [classes/:?]
Caused by: jdk.nashorn.internal.runtime.ECMAException: Error: error
	at jdk.nashorn.internal.objects.NativeError.initException(NativeError.java:137) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:102) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:106) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.<init>(NativeError.java:110) ~[nashorn.jar:?]
	at jdk.nashorn.internal.objects.NativeError.constructor(NativeError.java:129) ~[nashorn.jar:?]
	at jdk.nashorn.internal.scripts.Script$Recompilation$19665$15$\^eval\_.test(<eval>:1) ~[?:?]
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637) ~[nashorn.jar:?]
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494) ~[nashorn.jar:?]
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199) ~[nashorn.jar:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:386) ~[nashorn.jar:?]
	... 2 more
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create byte arrau at JPLISAgent.c line: 813
Exception in thread "main" java.lang.InternalError: BMH.reinvoke=Lambda(a0:L/SpeciesData<LIL>,a1:L)=>{
    t2:L=BoundMethodHandle$Species_LIL.argL2(a0:L);
    t3:I=BoundMethodHandle$Species_LIL.argI1(a0:L);
    t4:L=BoundMethodHandle$Species_LIL.argL0(a0:L);
    t5:L=MethodHandle.invokeBasic(t4:L,t2:L,a1:L,t3:I);t5:L}
	at java.lang.invoke.MethodHandleStatics.newInternalError(MethodHandleStatics.java:127)
	at java.lang.invoke.LambdaForm.compileToBytecode(LambdaForm.java:660)
	at java.lang.invoke.LambdaForm.prepare(LambdaForm.java:635)
	at java.lang.invoke.MethodHandle.<init>(MethodHandle.java:461)
	at java.lang.invoke.BoundMethodHandle.<init>(BoundMethodHandle.java:58)
	at java.lang.invoke.BoundMethodHandle$Species_LIL.<init>(Species_LIL)
	at java.lang.invoke.BoundMethodHandle$Species_LIL.make(Species_LIL)
	at java.lang.invoke.BoundMethodHandle$Species_LI.copyWithExtendL(Species_LI)
	at java.lang.invoke.LambdaFormEditor.bindArgumentL(LambdaFormEditor.java:404)
	at java.lang.invoke.BoundMethodHandle.bindArgumentL(BoundMethodHandle.java:99)
	at java.lang.invoke.MethodHandles.insertArguments(MethodHandles.java:2379)
	at jdk.internal.dynalink.ChainedCallSite.makePruneAndInvokeMethod(ChainedCallSite.java:226)
	at jdk.internal.dynalink.ChainedCallSite.relinkInternal(ChainedCallSite.java:184)
	at jdk.internal.dynalink.ChainedCallSite.relink(ChainedCallSite.java:149)
	at jdk.nashorn.internal.runtime.linker.LinkerCallSite.relink(LinkerCallSite.java:142)
	at jdk.internal.dynalink.DynamicLinker.relink(DynamicLinker.java:285)
	at jdk.nashorn.internal.scripts.Script$19666$\^eval\_.:program(<eval>:1)
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637)
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494)
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:449)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:406)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:402)
	at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at com.lt.test.log4j.TestJsEngine.main(TestJsEngine.java:27)
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
	at jdk.internal.org.objectweb.asm.ClassWriter.newUTF8(ClassWriter.java:1122)
	at jdk.internal.org.objectweb.asm.ClassWriter.newNameTypeItem(ClassWriter.java:1591)
	at jdk.internal.org.objectweb.asm.ClassWriter.newNameType(ClassWriter.java:1574)
	at jdk.internal.org.objectweb.asm.ClassWriter.newMethodItem(ClassWriter.java:1436)
	at jdk.internal.org.objectweb.asm.MethodWriter.visitMethodInsn(MethodWriter.java:917)
	at java.lang.invoke.InvokerBytecodeGenerator.emitStaticInvoke(InvokerBytecodeGenerator.java:867)
	at java.lang.invoke.InvokerBytecodeGenerator.generateCustomizedCodeBytes(InvokerBytecodeGenerator.java:715)
	at java.lang.invoke.InvokerBytecodeGenerator.generateCustomizedCode(InvokerBytecodeGenerator.java:618)
	at java.lang.invoke.LambdaForm.compileToBytecode(LambdaForm.java:654)
	at java.lang.invoke.LambdaForm.prepare(LambdaForm.java:635)
	at java.lang.invoke.MethodHandle.<init>(MethodHandle.java:461)
	at java.lang.invoke.BoundMethodHandle.<init>(BoundMethodHandle.java:58)
	at java.lang.invoke.BoundMethodHandle$Species_LIL.<init>(Species_LIL)
	at java.lang.invoke.BoundMethodHandle$Species_LIL.make(Species_LIL)
	at java.lang.invoke.LambdaForm$DMH/221634215.invokeStatic_L3IL_L(LambdaForm$DMH)
	at java.lang.invoke.BoundMethodHandle$Species_LI.copyWithExtendL(Species_LI)
	at java.lang.invoke.LambdaFormEditor.bindArgumentL(LambdaFormEditor.java:404)
	at java.lang.invoke.BoundMethodHandle.bindArgumentL(BoundMethodHandle.java:99)
	at java.lang.invoke.MethodHandles.insertArguments(MethodHandles.java:2379)
	at jdk.internal.dynalink.ChainedCallSite.makePruneAndInvokeMethod(ChainedCallSite.java:226)
	at jdk.internal.dynalink.ChainedCallSite.relinkInternal(ChainedCallSite.java:184)
	at jdk.internal.dynalink.ChainedCallSite.relink(ChainedCallSite.java:149)
	at jdk.nashorn.internal.runtime.linker.LinkerCallSite.relink(LinkerCallSite.java:142)
	at jdk.internal.dynalink.DynamicLinker.relink(DynamicLinker.java:285)
	at java.lang.invoke.LambdaForm$DMH/1976870338.invokeSpecial_LLIL_L(LambdaForm$DMH)
	at java.lang.invoke.LambdaForm$BMH/645206471.reinvoke(LambdaForm$BMH)
	at java.lang.invoke.LambdaForm$MH/480903748.exactInvoker(LambdaForm$MH)
	at java.lang.invoke.LambdaForm$MH/756400524.linkToCallSite(LambdaForm$MH)
	at jdk.nashorn.internal.scripts.Script$19666$\^eval\_.:program(<eval>:1)
	at java.lang.invoke.LambdaForm$DMH/664223387.invokeStatic_LL_L(LambdaForm$DMH)
	at java.lang.invoke.LambdaForm$MH/1278254413.invokeExact_MT(LambdaForm$MH)
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637)

Reproduction

Below is my test case, with memory settings configured as -Xms10m -Xmx10m. The program runs for 2 minutes and 24 seconds before an OutOfMemoryError (OOM) occurs.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class TestJsEngine {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestJsEngine.class);

    public static void main(String[] args) {
        //Set the class cache size to 0
        System.setProperty("nashorn.option.class.cache.size", "0");
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine engine = engineManager.getEngineByName("javascript");
        String content = "function test(){" +
                    "throw new Error('error'); " +
                "}";
        while (true) {
            try {
                engine.eval(content);
            } catch (ScriptException e) {
                LOGGER.error("eval script exception", e);
            }
            Invocable in = (Invocable) engine;
            try {
                in.invokeFunction("test");
            } catch (ScriptException e) {
                LOGGER.error("Script execution exception", e);
            } catch (NoSuchMethodException e) {

            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions