cxf-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Joe Sunday <sun...@csh.rit.edu>
Subject Re: Memory leak in client?
Date Wed, 05 Sep 2007 17:42:14 GMT

On Sep 5, 2007, at 2:07 AM, Liu, Jervis wrote:

>
>
>> -----Original Message-----
>> From: Joe Sunday [mailto:sunday@csh.rit.edu]
>> Sent: 2007?9?5? 12:11
>> To: cxf-user@incubator.apache.org
>> Subject: Re: Memory leak in client?
>>
>>
>> I changed it so I only create the service object once and then each
>> time I need a new client use getMyPort and set it up.. The docs
>> aren't exactly clear, but it looks like I get distinct proxies back
>> on each call, so I'm assuming that's safe? If I then call
>> service.getMyPort() across multiple threads?
>>
>
> As you already noticed, getMyPort(which calls  
> org.apache.cxf.jaxws.ServiceImpl.createPort(QName,  
> EndpointReferenceType, Class<T>) indeed creates a new proxy object  
> every time, thus a new ServiceFactoryBean, a new service model etc  
> for every new proxy. If what you are trying to do is invoking a  
> same web service several times, you should try to use the same  
> proxy instead.

That's not always possible. The client is running inside a CXF  
service, and the endpoint and session to the remote end change over  
time. I shouldn't have to cache 100 ports for the life of the  
program. I re-use the same port where I can, but when I'm done with  
it it shouldn't keep holding memory.
>
>> It still leaks about 6 megs each time I call getMyPort() and make a
>> few remote requests. After any references I have to the new port
>> object are gone and I've forced a few GCs,it looks like a bunch of
>> data being left in a ThreadLocal. I'm still trying to unwind what
>> happens behind the scenes when I setup a client fully, but I see a
>> couple ThreadLocals in JaxWsClientProxy that might not be getting
>> cleared?
>>
>
> If you are sure all references to the proxy object have gone, and  
> there are still some objects not cleaned by GC, then it might be a  
> memory leak on the client side. Would you be able to reproduce same  
> symptoms using a very simple WSDL, for example, the hello_world  
> sample in CXF distribution?

Here's my test that approximates what I'm really doing... I've got a  
stack of worker threads that each one will occasionally create a port  
and a few requests, and then let it go. The threads stick around to  
get re-used. If I run this test, the following happens:

10 threads / 50 invokes() per thread:
1) Beginning execution 1,512k
2) Workers finished and idle 5,500k
3) Telling workers to die 5,500k
4) Idle state 1,736k

10 threads / 100 invokes() per thread:
1) Beginning execution 1,512k
2) Workers finished and idle 5,761k
3) Telling workers to die 5,761k
4) Idle state 1,732k

20 threads / 50 invokes() per thread:
1) Beginning execution 1,512k
2) Workers finished and idle 12,052k
3) Telling workers to die 10,814k
4) Idle state 2,268k

20 threads / 100 invokes() per thread:
1) Beginning execution 1,512k
2) Workers finished and idle 11,245k
3) Telling workers to die 9,983k
4) Idle state 2,403k

If I make invoke() a no-op as a baseline:
1) Beginning execution 1,522k
2) Workers finished and idle 1,520k
3) Telling workers to die 1,504k
4) Idle state 1,504k

I would expect for CXF, (3) would be around the same value as (4),  
since there should not be live references to the port objects hanging  
around at that point. It looks like something's getting put into a  
ThreadLocal that isn't getting collected until the thread dies even  
after I've lost the reference to the port object.

This also makes me wonder, if I am caching the port objects for a  
brief time, is it safe to pass control of a port from one thread to  
another? My actual worker operations may migrate threads between  
outgoing requests, so can I pass the port safely to the new thread?

package soap;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import java.util.concurrent.CountDownLatch;

public final class Greeter_SoapPort_Client {

     private static final QName SERVICE_NAME = new QName("http:// 
apache.org/hello_world_soap_http", "SOAPService");
     private static final URL WSDL = Greeter.class.getClassLoader 
().getResource("hello_world.wsdl");

     private Greeter_SoapPort_Client() {
     }

     public static void main(String args[]) throws Exception {
         int tCount = 10;
         CountDownLatch latch = new CountDownLatch(tCount);

         SOAPService ss = new SOAPService(WSDL, SERVICE_NAME);
         System.out.println("Waiting to attach jconsole");
         try { Thread.sleep(7 * 1000); }
         catch (InterruptedException e) { }

         System.out.println("1) Beginning execution " + mem());

         Thread[] workers = new Thread[tCount];
         for (int i = 0; i < tCount; i++) {
             workers[i] = new Thread(
                     new Worker(ss, latch));
             workers[i].setName("Worker " + i);
             workers[i].start();
         }

         try { latch.await(); }
         catch (InterruptedException e) { }

         System.out.println("2) Workers finished and idle " + mem());

         try { Thread.sleep(10 * 1000); }
         catch (InterruptedException e) { }

         System.out.println("3) Telling workers to die " + mem());
         for (int i = 0; i < tCount; i++) {
             synchronized (workers[i]) {
                 workers[i].notify();
                 workers[i] = null;
             }
         }

         try { Thread.sleep(10 * 1000); }
         catch (InterruptedException e) { }

         System.out.println("4) Idle state " + mem());
     }

     public static String mem() {
         System.gc();
         System.gc();
         long b = Runtime.getRuntime().totalMemory() -  
Runtime.getRuntime().freeMemory();
         return String.format("%,dk", b / 1024);

     }

     private static class Worker implements Runnable {
         SOAPService ss;
         CountDownLatch latch;

         Worker(SOAPService ss, CountDownLatch latch) {
             this.ss = ss;
             this.latch = latch;
         }

         public void run() {
             for (int i = 0; i < 100; i++) {
                invoke();
             }
             latch.countDown();
             synchronized (Thread.currentThread()) {
                 try { Thread.currentThread().wait(); }
                 catch (InterruptedException e) { }
             }
         }

         // Force this into a separate stack frame
         public void invoke() {
             Greeter port = ss.getSoapPort();
             String ret = port.sayHi();
         }
     }
}


--Joe


Mime
View raw message