You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@logging.apache.org by "Volkan Yazici (Jira)" <ji...@apache.org> on 2023/03/24 21:17:00 UTC

[jira] [Assigned] (LOG4J2-3657) Log4j memory leak via Classloader

     [ https://issues.apache.org/jira/browse/LOG4J2-3657?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Volkan Yazici reassigned LOG4J2-3657:
-------------------------------------

    Assignee: Volkan Yazici

> Log4j memory leak via Classloader
> ---------------------------------
>
>                 Key: LOG4J2-3657
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-3657
>             Project: Log4j 2
>          Issue Type: Bug
>          Components: API
>    Affects Versions: 2.20.0
>            Reporter: Marat Kamalov
>            Assignee: Volkan Yazici
>            Priority: Critical
>         Attachments: image-2023-03-24-21-05-55-700.png, image-2023-03-24-21-07-04-937.png
>
>
> We have a memory leak in the application. If we stop and run Jetty in the loop in JUnit we get OOM. 
> The issue is in the following lines. I think Log4j attaches an object to the thread via the _get_ method (because the field is defined like this:
> {code:java}
> private static final ThreadLocal<DefaultLogBuilder> logBuilder = ThreadLocal.withInitial(DefaultLogBuilder::new);{code}
> It means that Log4j attaches an object to a thread {*}even _Constants.ENABLE_THREADLOCALS_ is disabled{*}. My thoughts:
> 1) Log4j code attaches _DefaultLogBuilder_ object to threads
> 2) _DefaultLogBuilder_ object has a link to _DefaultLogBuilder.class_ that has a link to _DefaultLogBuilder.class.getClassloader()_ 
> 3) _DefaultLogBuilder.class.getClassloader()_ is a Web App Classloader and during our JUnit testing many classes are loaded to the classloader
> 4) I suspect somewhere some Thread Pool is used and even if we clean all resources in the application, Log4j *holds classloaders and all loaded classes* via _DefaultLogBuilder.class.getClassloader()_
>  
> I suggest checking _Constants.ENABLE_THREADLOCALS_ variable before calling _logBuilder.get()_ in _org.apache.logging.log4j.spi.AbstractLogger._ *After the following changes, the problem is gone.* (except additionally I added {_}log4j2.disable.jmx=true{_}, because it has a similar issue, but I think it is not a log4j issue)
> {code:java}
> /**
>  * Returns a log builder that logs at the specified level.
>  *
>  * @since 2.20.0
>  */
> protected LogBuilder getLogBuilder(Level level) {
>     if (Constants.ENABLE_THREADLOCALS) {
>         DefaultLogBuilder builder = logBuilder.get();
>         if (!builder.isInUse()) {
>             return builder.reset(this, level);
>         }
>     }
>     return new DefaultLogBuilder(this, level);
> } {code}
> Original code:
> {code:java}
> /**
>  * Returns a log builder that logs at the specified level.
>  *
>  * @since 2.20.0
>  */
> protected LogBuilder getLogBuilder(Level level) {
>     DefaultLogBuilder builder = logBuilder.get();
>     return Constants.ENABLE_THREADLOCALS && !builder.isInUse() ? builder.reset(this, level)
>             : new DefaultLogBuilder(this, level);
> } {code}
> P.S. Please, if it is possible can you include the fix in an upcoming release, the issue randomly happens during our application build and it is a headache for us :) Thank you.
> P.S.S. We use a free license of Yourkit for open-source projects. Please, look at the picture from a profiler.
> !image-2023-03-24-21-05-55-700.png!



--
This message was sent by Atlassian Jira
(v8.20.10#820010)