apache / apache/logging-log4j2

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

Open
#3,282 11 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
3.6k
Forks
1.7k
Avg merge
21h 30m
Merged PRs (30d)
27

Description

## 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 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.(NativeError.java:102) ~[nashorn.jar:?]
at jdk.nashorn.internal.objects.NativeError.(NativeError.java:106) ~[nashorn.jar:?]
at jdk.nashorn.internal.objects.NativeError.(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(: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 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.(NativeError.java:102) ~[nashorn.jar:?]
at jdk.nashorn.internal.objects.NativeError.(NativeError.java:106) ~[nashorn.jar:?]
at jdk.nashorn.internal.objects.NativeError.(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(: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,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.(MethodHandle.java:461)
at java.lang.invoke.BoundMethodHandle.(BoundMethodHandle.java:58)
at java.lang.invoke.BoundMethodHandle$Species_LIL.(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(: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.(MethodHandle.java:461)
at java.lang.invoke.BoundMethodHandle.(BoundMethodHandle.java:58)
at java.lang.invoke.BoundMethodHandle$Species_LIL.(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(: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.

```java
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) {

}
}
}
}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the ThrowableProxy class and trace its handling of exception stack-trace class names. Run the supplied Nashorn reproduction with the stated JDK and memory settings, then compare behavior before and after a change. Done means repeated exception logging no longer leads to the reported memory exhaustion, with regression coverage for the scenario.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.