cxf-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Kulp <dk...@apache.org>
Subject Re: Memory leak in client?
Date Thu, 06 Sep 2007 19:21:10 GMT

Joe,

One thing to keep in mind:  the Bus would still be around.  You'd need to 
get the default Bus and call shutdown() on it if you're completely done.  

Dan


On Wednesday 05 September 2007, Joe Sunday wrote:
> 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



-- 
J. Daniel Kulp
Principal Engineer
IONA
P: 781-902-8727    C: 508-380-7194
daniel.kulp@iona.com
http://www.dankulp.com/blog

Mime
View raw message