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

[jira] [Updated] (LOG4J2-3657) Log4j memory leak via multiple classloaders

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

Marat updated LOG4J2-3657:
--------------------------
    Description: 
We have a memory leak in the application. If we stop and run Jetty in the loop in JUnit we get OOM. 

I think the issue is in the following lines, but I didn't try to fix it. I think you attach an object to the thread via the get method (because the field is defined like this:
{code:java}
ThreadLocal.withInitial(DefaultLogBuilder::new) {code}
It means that you attach an object to a thread even Constants.ENABLE_THREADLOCALS is disabled. My thoughts (but as I said I didn't check it):

1) Log4j code attaches DefaultLogBuilder object to the thread
2) For creating DefaultLogBuilder object -> DefaultLogBuilder class is used and linked to the object -> for loading the class DefaultLogBuilder.class.getClassloader() is required and attached to the initial object

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 via DefaultLogBuilder.class.getClassloader()
 
I suggest checking Constants.ENABLE_THREADLOCALS variable before calling logBuilder.get(). Please, check all the other places.
 
!image-2023-03-24-21-07-04-937.png!

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!

  was:
We have a memory leak in the application. If we stop and run Jetty in the loop in JUnit we get OOM. 

I think the issue is in the following lines, but I didn't try to fix it. I think you attach an object to the thread via the get method (because the field is defined like
ThreadLocal.withInitial(DefaultLogBuilder::new);) It means that you attach an object to the thread even Constants.ENABLE_THREADLOCALS is disabled. My thoughts (but as I said I didn't check it):
1) Log4j code attaches DefaultLogBuilder to the thread
2) For creating DefaultLogBuilder, DefaultLogBuilder.class.getClassloader() is used 
3) During our application working classes are loaded to this classloader
4) I suspect somewhere some Thread Pool is used and even if we clean all resources in the application, Log4j holds classloaders via DefaultLogBuilder.class.getClassloader()
 
I suggest checking Constants.ENABLE_THREADLOCALS variable before calling
logBuilder.get(). Please, check all other places.
 
!image-2023-03-24-21-07-04-937.png!

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!


> Log4j memory leak via multiple classloaders
> -------------------------------------------
>
>                 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
>            Priority: Major
>         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. 
> I think the issue is in the following lines, but I didn't try to fix it. I think you attach an object to the thread via the get method (because the field is defined like this:
> {code:java}
> ThreadLocal.withInitial(DefaultLogBuilder::new) {code}
> It means that you attach an object to a thread even Constants.ENABLE_THREADLOCALS is disabled. My thoughts (but as I said I didn't check it):
> 1) Log4j code attaches DefaultLogBuilder object to the thread
> 2) For creating DefaultLogBuilder object -> DefaultLogBuilder class is used and linked to the object -> for loading the class DefaultLogBuilder.class.getClassloader() is required and attached to the initial object
> 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 via DefaultLogBuilder.class.getClassloader()
>  
> I suggest checking Constants.ENABLE_THREADLOCALS variable before calling logBuilder.get(). Please, check all the other places.
>  
> !image-2023-03-24-21-07-04-937.png!
> 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)