[ 
https://issues.apache.org/jira/browse/CXF-9146?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17975824#comment-17975824
 ] 

Eric commented on CXF-9146:
---------------------------

That's not really an option we want to take any longer if it is possible to 
avoid it.

We have a large stack of software and doing so would require us to add jvm 
Options to almow reflection an URLConnection again in order to support all 
Httpmethods.

Instead I will see if I can eliminate the dangling reference to 
PipedOutputStream in the HttpClientWrappedInputStream above. This solves these 
bugs without touching other classes and seems to be the root of the problem.

I guess all that is missing is the following line in 
HttpClientWrappedOutputStream::close:

 
{code:java}
wrappedOutputStream = null{code}
This removes the last reference to the PipedOutputStream and the GC can do its 
work in the acceptancetest above and remove the dead Thread and Context objects.

 

The best part ist that in this case I can write a custom HttpConduitFactory, 
register it in the Bus and overwrite the 
HttpClientHTTPConduit::createOutputStream with a custom close method to fix the 
bug in our application until the issue is fixed in CXF itself.

 

> MemoryLeak caused by ThreadLocalClientState when used with the 
> HttpClientConduit
> --------------------------------------------------------------------------------
>
>                 Key: CXF-9146
>                 URL: https://issues.apache.org/jira/browse/CXF-9146
>             Project: CXF
>          Issue Type: Bug
>            Reporter: Eric
>            Priority: Major
>         Attachments: CxfClientMemoryLeakTest.java, 
> image-2025-06-10-19-04-43-021.png, image-2025-06-10-19-05-03-593.png
>
>
> {color:#000000}Is defined as following:{color}
> {code:java}
> public class ThreadLocalClientState implements ClientState {
>        private Map<Thread, LocalClientState> state = 
> Collections.synchronizedMap(new WeakHashMap<Thread, LocalClientState>()); 
> {code}
> ... which does not make any sense to me, as is this is just ThreadLocal built 
> at home, a class which as been in the JDK for a very long time.. A 
> WeakHashMap of Threads might emulate ThreadLocal,  but it can cause 
> unexpected memory leaks, because the Weak-Keys are strongly held until the 
> WeakHashMap is accessed for the next time.
>  
> Let's take this simple example:
> {code:java}
> var mb = 1024 * 1024;
> var rt = Runtime.getRuntime();
> var entries = new WeakHashMap<Object, byte[][]>();
> var keys = new ArrayList<>();
> System.gc();
> System.out.printf("Memory before Memory Load: %s mb%n", (rt.totalMemory() - 
> rt.freeMemory()) / mb);
> IntStream.range(0, 100).mapToObj(i -> new Date(i + 1000)).forEach(key -> {
>     keys.add(key);
>     entries.put(key, new byte[1][10 * mb]);
> });
> System.gc();
> System.out.printf("Memory after Memory Load: %s mb%n", (rt.totalMemory() - 
> rt.freeMemory()) / mb);
> keys.clear();
> System.gc();
> System.out.printf("Memory after Memory Keys clear: %s mb%n", 
> (rt.totalMemory() - rt.freeMemory()) / mb);
> System.out.printf("Map Size: %s%n", entries.size());
> System.gc();
> System.out.printf("Memory after first Memory Map access: %s mb%n", 
> (rt.totalMemory() - rt.freeMemory()) / mb);{code}
>  
> This leads to the following output:
> {noformat}
> Memory before Memory Load: 13 mb
> Memory after Memory Load: 1213 mb
> Memory after Memory Keys clear: 1213 mb
> Map Size: 0
> Memory after first Memory Map access: 13 mb
> {noformat}
>  
> Please ignore my inaccurate measurements with System.gc() without using JMH, 
> but when looking up the source for WeakHashMap then it becomes obvious that 
> no cleanup is technically possible until the WeakHashMap is deferenced for 
> the next time for a method of the Map interface. This is a scenario which 
> should rarely happen in a production enviroment, but we have seen it at least 
> once being the cause of an OutOfMemoryError wenn many threads in in parallel 
> executed requests which returned very large Response objects in memory.
> Therefore, and especially when thinking of the future of virtual threads and 
> scoped values, I would advise to the refactor the code so that it internally 
> uses a simple Treadlocal from now on instead of the WeakHashMap.



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

Reply via email to