Hello Mark,

> -----Ursprüngliche Nachricht-----
> Von: Mark Eggers <its_toas...@yahoo.com.INVALID>
> Gesendet: Montag, 28. März 2022 23:55
> An: users@tomcat.apache.org
> Betreff: Re: AW: AW: AW: Question to possible memory leak by Threadlocal
> variable
> 
> Thomas:
> 
> On 3/28/2022 2:01 PM, Thomas Hoffmann (Speed4Trade GmbH) wrote:
> > Hello Chris,
> >
> >> -----Ursprüngliche Nachricht-----
> >> Von: Christopher Schultz <ch...@christopherschultz.net>
> >> Gesendet: Montag, 28. März 2022 18:48
> >> An: users@tomcat.apache.org
> >> Betreff: Re: AW: AW: Question to possible memory leak by Threadlocal
> >> variable
> >>
> >> Thomas,
> >>
> >> On 3/25/22 16:59, Thomas Hoffmann (Speed4Trade GmbH) wrote:
> >>>> -----Ursprüngliche Nachricht-----
> >>>> Von: Christopher Schultz <ch...@christopherschultz.net>
> >>>> Gesendet: Freitag, 25. März 2022 14:05
> >>>> An: users@tomcat.apache.org
> >>>> Betreff: Re: AW: Question to possible memory leak by Threadlocal
> >>>> variable
> >>>>
> >>>> Thomas,
> >>>>
> >>>> On 3/24/22 05:49, Thomas Hoffmann (Speed4Trade GmbH) wrote:
> >>>>>
> >>>>>
> >>>>>> -----Ursprüngliche Nachricht-----
> >>>>>> Von: Mark Thomas <ma...@apache.org>
> >>>>>> Gesendet: Donnerstag, 24. März 2022 09:32
> >>>>>> An: users@tomcat.apache.org
> >>>>>> Betreff: Re: Question to possible memory leak by Threadlocal
> >>>>>> variable
> >>>>>>
> >>>>>> On 24/03/2022 07:57, Thomas Hoffmann (Speed4Trade GmbH)
> wrote:
> >>>>>>
> >>>>>> <snip/>
> >>>>>>
> >>>>>>> Is it correct, that every spawned thread must call tl.remove()
> >>>>>>> to cleanup all
> >>>>>> the references to prevent the logged warning (and not only the
> >>>>>> main thread)?
> >>>>>>
> >>>>>> Yes. Or the threads need to exit.
> >>>>>>
> >>>>>>> Second question is: How might it cause a memory leak?
> >>>>>>> The threads are terminated and hold a reference to this static
> >>>>>>> variable. But
> >>>>>> on the other side, that class A is also eligible for garbage
> >>>>>> collection after undeployment.
> >>>>>>> So both, the thread class and the class A are ready to get
> >>>>>>> garbage collected. Maybe I missed something (?)
> >>>>>>
> >>>>>> It sounds as if the clean-up is happening too late. Tomcat
> >>>>>> expects clean-up to be completed once contextDestroyed() has
> >>>>>> returned for all ServLetContextListeners. If the clean-up is
> >>>>>> happening asynchronously
> >>>> (e.g.
> >>>>>> the call is made to stop the threads but doesn't wait until the
> >>>>>> threads have
> >>>>>> stopped) you could see this message.
> >>>>>>
> >>>>>> In this case it sounds as if you aren't going to get a memory
> >>>>>> leak but Tomcat can't tell that at the point it checks.
> >>>>>>
> >>>>>> Mark
> >>>>>>
> >>>>>> -----------------------------------------------------------------
> >>>>>> --
> >>>>>> -- To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> >>>>>> For additional commands, e-mail: users-h...@tomcat.apache.org
> >>>>>
> >>>>> Hello Mark,
> >>>>> thanks for the information.
> >>>>> The shutdown of the framework is currently placed within the
> >>>>> destroy()
> >>>> method of a servlet (with load on startup).
> >>>>> At least the debugger shows that servlet-->destroy() is executed
> >>>>> before
> >>>> the method checkThreadLocalMapForLeaks() runs.
> >>>>> I will take a look, whether the threads already exited.
> >>>>
> >>>> Tomcat only checks its own request-processing threads for
> >>>> ThreadLocals, so any threads created by the application or that
> >>>> library are unrelated to the warning you are seeing.
> >>>>
> >>>> Any library which saves ThreadLocals from request-processing
> >>>> threads is going to have this problem if the objects are of types
> >>>> loaded by the webapp ClassLoader.
> >>>>
> >>>> There are a few ways to mitigate this, but they are ugly and it
> >>>> would be better if the library didn't use ThreadLocal storage, or
> >>>> if it would use vanilla classes from java.* and not its own types.
> >>>>
> >>>> You say that those objects are eligible for GC after the library
> >>>> shuts down, but that's not true: anything you stick in ThreadLocal
> >>>> storage
> >> is being held ...
> >>>> by the ThreadLocal storage and won't be GC'd. If an object can't be
> >>>> collected, the java.lang.Class defining it can't be collected, and
> >>>> therefore the ClassLoader which loaded it (the webapp
> >>>> ClassLoader) can't be free'd. We call this a "pinned ClassLoader"
> >>>> and it still contains all of the java.lang.Class instances that the
> >>>> ClassLoader ever loaded during its lifetime. If you reload
> >>>> repeatedly, you'll see un-collectable ClassLoader instances piling
> >>>> up in memory which is
> >>>> *definitely* a leak.
> >>>>
> >>>> The good news for you is that Tomcat has noticed the problem and
> >>>> will, over time, retire and replace each of the affected Threads in
> >>>> its request- processing thread pool. As those Thread objects are
> >>>> garbage-collected, the TheradLocal storage for each is also
> >>>> collected, etc. and *eventually* your leak will be resolved. But it
> >>>> would be
> >> better not to have one in the first place.
> >>>>
> >>>> Why not name the library? Why anonymize the object type if it's
> >>>> org.apache.something?
> >>>>
> >>>> -chris
> >>>
> >>> Hello Chris,
> >>> I didn't want to blame any library 😉 But as you ask for it, I send
> >>> more
> >> details.
> >>>
> >>> Regarding the ThreadLocal thing:
> >>> I thought that the threadlocal variables are stored within the
> >>> Thread-class in the member variable "ThreadLocal.ThreadLocalMap
> >>> threadLocals":
> >>> https://github.com/AdoptOpenJDK/openjdk-
> >> jdk11/blob/master/src/java.bas
> >>> e/share/classes/java/lang/Thread.java
> >>>
> >>> So I thought, when the thread dies, these variables will also be
> >>> released and automatically removed from the ThreadLocal variable /
> >>> instance (?)
> >> This is correct, but if the ThreadLocal is being stored in the
> >> request- processing thread, then when your web application is
> >> redeployed, the request processing threads outlive that event. Maybe
> >> you thought your application gets a private set of threads for its
> >> own use, but that's not the
> >> case: Tomcat pools threads across all applications deployed on the server.
> >> You can play some games to segregate some applications from others,
> >> but it's a lot of work for not much gain IMO.
> >>
> >> Since the threads outlive the application, you can see the problem, now.
> >>
> >>> I considered the ThreadLocal class as just the manager of the
> >>> thread's member variable "threadLocals".
> >> Basically, yes.
> >>
> >>> Regarding the library:
> >>> The full log-message is:
> >>> 12-Mar-2022 15:01:16.302 SCHWERWIEGEND [Thread-15]
> >>
> org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapF
> >> orLeaks The web application [ROOT] created a ThreadLocal with key of
> >> type [java.lang.ThreadLocal.SuppliedThreadLocal] (value
> >> [java.lang.ThreadLocal$SuppliedThreadLocal@2121cbad]) and a value of
> >> type [org.apache.camel.impl.DefaultCamelContext.OptionHolder] (value
> >> [org.apache.camel.impl.DefaultCamelContext$OptionHolder@338d0413])
> >> but failed to remove it when the web application was stopped. Threads
> >> are going to be renewed over time to try and avoid a probable memory
> leak.
> >>   >
> >>> The blamed class is this version:
> >>> https://github.com/apache/camel/blob/camel-3.14.x/core/camel-core-
> >> engi
> >>> ne/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
> >>
> >> Interesting that Camel is storing a ThreadLocal. Maybe there is a
> >> better way to use Camel in the context of a web application?
> >>
> >>> Within our app we have a startup servlet with:
> >>> Servlet-> init: context = new DefaultCamelContext();
> >>> Servlet -> destroy: context.stop();
> >>>
> >>> The stop-method will call the doStop() method of this class (via the
> >>> class hierarchy DefaultCamelContext --> SimpleCamelContext -->
> >>> AbstractCamelContext). > After the destroy-method is executed, all
> >>> spawned threads of Camel are stopped / vanished. There is also no
> >>> log entry, that some orphaned threads exist when undeploying the app.
> >>>
> >>> So I don’t know, what's the mistake within this class. What would be
> >>> the right way to clean up the ThreadLocal variable? Just stopping
> >>> the threads didn’t seem to clean it up properly.
> >> The saved ThreadLocal was done from within one of the
> >> request-processing threads that Tomcat owns. This wasn't a thread
> >> spawned by the library, which is likely to already be cleaned-up when
> >> stop() is completed, as expected.
> >>
> >> It looks like Camel may be capturing some values and storing them in
> >> ThreadLocal when it doesn't make sense (in a web application) to do so.
> >>
> >> Are you able to instrument your application to see when those
> >> ThreadLocals are set?
> >>
> >> -chris
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> >> For additional commands, e-mail: users-h...@tomcat.apache.org
> >
> > I think I understand now, how the memory leak is created.
> > So lets say Tomcat has three worker Threads W1, W2 and W3.
> > If every one of them is using the CamelContext, then all of them will 
> > inherit
> this ThreadLocal-Value within their worker threads.
> >
> > I will debug into the library and try to confirm this.
> >
> > Thanks to your explanation, the leak report makes sense to me now.
> > Right now I don’t have a clue, how all the workers might release that
> ThreadLocal variable.
> >
> > I will let you know, what the debugger says.
> >
> > Thanks! Thomas
> 
> This was just released:
> 
> https://camel.apache.org/releases/release-3.16.0/
> 
> And according to the release notes:
> 
> https://issues.apache.org/jira/browse/CAMEL-17712
> 
> was addressed.
> 
> Is this useful?
> 
> . . . just my two cents
> /mde/

Thanks for your message. This bug report was filed by me :)
As the new version didn’t fix the memory leak completely, I investigated it 
further.
The patch only removes the threadlocal variable form the current thread.

I was on the wrong track because I thought the spawned threads by Camel created 
the memory leak.
But as Chris explained, the worker threads get "polluted" by the threadlocal 
variables.

Now that I understood the real cause, I will investigate the problem a bit 
further and try to get in touch with the Camel developers.
Before reporting an update to this problem, I needed to understand the problem. 
Thanks to this user group, its much clearer now :)

Thanks! Thomas

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to