On Sep 5, 2007, at 2:07 AM, Liu, Jervis wrote:
-----Original Message-----
From: Joe Sunday [mailto:[EMAIL PROTECTED]
Sent: 2007?9?5? 12:11
To: [email protected]
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