4

I have a java webobjects app which is showing memory leak problems when running on Red Hat but we had no such problems when it was running on Mac OS X. The JVMs are similar.

Mac OS X 10.6.5 using java 1.6.0_22 64 bit from Apple Red Hat EL 5.0 using java 1.6.0_20 64 bit from Sun

I configured it to do a heap dump when it ran out of memory, and analysing this with the eclipse memory analyzer tool suggests that the problem is in a part of the code which creates a thread which sends an HTTP Request to a web service. The reason for creating the thread is to implement a timeout on the request because the web service is sometimes not available.

Does anyone have any ideas?

    WOHTTPConnection connection = new WOHTTPConnection(host, port);
    WORequest request = new WORequest(strMethod, strQuery, strHttpVersion, nsdHeader, content, null);

    WebServiceRequester theRequester = new WebServiceRequester(connection, request);
    Thread requestThread = new Thread(theRequester);
    requestThread.start();
    try {
            requestThread.join(intTimeoutSend);  //timeout in milliseconds = 10000
            if ( requestThread.isAlive() ) {
                requestThread.interrupt();
            }
    } catch(InterruptedException e) { 

    }
    requestThread = null;
    if(!theRequester.getTfSent()) {
           return null;
    }
    WOResponse response = connection.readResponse();

...

class WebServiceRequester implements Runnable {

    private WORequest theRequest;
    private WOHTTPConnection theConnection;
    private boolean tfSent = false;

    public WebServiceRequester(WOHTTPConnection c, WORequest r) {
        theConnection = c;
        theRequest = r;
    }

    public void run() {
        tfSent = theConnection.sendRequest(theRequest);
    }

    public boolean getTfSent() {
        return tfSent;
    }
}

EDIT: leaked class names as reported by eclipse memory analyzer tool:

1,296 instances of "java.lang.Thread", loaded by "<system class loader>" occupy 111,947,632 (43.21%) bytes.
1,292 instances of "er.extensions.eof.ERXEC", loaded by "java.net.URLClassLoader @ 0x2aaab375b7c0" occupy 37,478,352 (14.46%) bytes.
1,280 instances of "er.extensions.appserver.ERXRequest", loaded by "java.net.URLClassLoader @ 0x2aaab375b7c0" occupy 27,297,992 (10.54%) bytes.
Rudiger
  • 6,749
  • 13
  • 51
  • 102

3 Answers3

1

Do you need to close the WOHTTPConnection handle? (I'm not familiar with that API ...).

FOLLOWUP

Checked into it, looks like the connection.readResponse() closes the connection so I don't need to do it manually.

@Rudiger - you are assuming that the call to connection.readResponse() always succeeds. What if the problem is that it is throwing an exception that is not getting reported. (The default behavior is to silently ignore errors thrown on child threads.)

I think you should close the connection handle in a finally block ... just in case.

Or better still, ditch WOHTTPConnection entirely.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Checked into it, looks like the connection.readResponse() closes the connection so I don't need to do it manually. – Rudiger Dec 01 '10 at 04:55
1

I think the problem is that Thread.interrupt does not actually stop your Thread. And the JVM will never clean up the thread if its running.

I would add a closeConnection method to your thread and try calling that instead of or in addition to your Thread.interrupt call. You might need to modify it a bit, but the idea is to explicitly stop the IO that is keeping the Thread running:

WOHTTPConnection connection = new WOHTTPConnection(host, port);
WORequest request = new WORequest(strMethod, strQuery, strHttpVersion, nsdHeader, content, null);

WebServiceRequester theRequester = new WebServiceRequester(connection, request);
Thread requestThread = new Thread(theRequester);
requestThread.start();
try {
        requestThread.join(intTimeoutSend);  //timeout in milliseconds = 10000
        if ( requestThread.isAlive() ) {
            requestThread.closeConnection();
            requestThread.interrupt();
        }
} catch(InterruptedException e) { 

}
requestThread = null;
if(!theRequester.getTfSent()) {
       return null;
}
WOResponse response = connection.readResponse();

...

class WebServiceRequester implements Runnable {

    private WORequest theRequest;
    private WOHTTPConnection theConnection;
    private boolean tfSent = false;

    public WebServiceRequester(WOHTTPConnection c, WORequest r) {
        theConnection = c;
        theRequest = r;
    }

    public void run() {
        tfSent = theConnection.sendRequest(theRequest);
    }

    public boolean getTfSent() {
        return tfSent;
    }

    public void closeConnection() {
        this.theConnection.close();
    }

}

romacafe
  • 3,098
  • 2
  • 23
  • 27
  • Looking into it further there is a case that the connection may remain open, unfortunately you can't call the method to close the connection because its private. There is a problem with this class in general and shouldn't be used. – Rudiger Dec 02 '10 at 01:58
1

I have heard that WOHTTPConnection is broken and shouldn't be used. WOHTTPConnection does not give you a reliable way of closing the connection. It is also unreliable in other ways.

The solution is to rewrite the code using Apache HttpComponents HttpClient

timcu
  • 56
  • 2