在 Red Hat 上运行时出现 Java 内存泄漏,但在 Mac OS X 上没有内存泄漏。

4
我有一个Java WebObjects应用程序,在Mac OS X上运行时没有内存泄漏问题,但在Red Hat上运行时存在此类问题。JVM相似。
Mac OS X 10.6.5使用来自Apple的64位Java 1.6.0_22, Red Hat EL 5.0使用来自Sun的64位Java 1.6.0_20。
我配置了它在内存不足时执行堆转储,并使用Eclipse Memory Analyzer工具分析表明问题出现在创建一个线程以向Web服务发送HTTP请求的代码部分。创建线程的原因是实现对请求的超时,因为Web服务有时无法访问。
请问有人有任何想法吗?
    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;
    }
}

编辑:根据Eclipse内存分析工具报告的泄露类名:

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.

如果你摆脱线程,让它正常运行,内存泄漏会消失吗?如果不是,那么这就不是你的问题。 - James Black
检查过了,不行,它还在那里 :( - Rudiger
1
泄露的类名是什么? - J-16 SDiZ
你尝试过OpenJDK吗?如果不使用线程运行它会发生什么?是否仍然有1296个线程占用内存? - Denis Tulskiy
3个回答

1

您需要关闭WOHTTPConnection句柄吗?(我不熟悉该API...)。

后续

已经检查过了,看起来连接.readResponse()会关闭连接,所以我不需要手动操作。

@Rudiger - 您假设调用connection.readResponse() 总是成功。如果问题是它抛出一个没有被报告的异常怎么办呢?(默认行为是在子线程上静默忽略错误。)

我认为您应该在finally块中关闭连接句柄...以防万一。

或者更好的方法是完全放弃WOHTTPConnection


我已经检查过了,看起来connection.readResponse()会关闭连接,所以我不需要手动关闭它。 - Rudiger

1

我听说WOHTTPConnection已经失效,不应该再使用。WOHTTPConnection不能提供可靠的关闭连接方式,而且在其他方面也不可靠。

解决方案是使用Apache HttpComponents HttpClient重写代码。


1

我认为问题在于Thread.interrupt并不能真正停止你的线程。如果线程正在运行,JVM将永远不会清理它。

我建议在你的线程中添加一个closeConnection方法,并尝试调用它来代替或者附加到Thread.interrupt调用上。你可能需要对其进行一些修改,但是这个想法是明确地停止保持线程运行的IO:

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();
    }

}


进一步研究后发现,连接可能仍然保持打开状态,不幸的是,您无法调用关闭连接的方法,因为它是私有的。这个类存在问题,一般情况下不应该使用。 - Rudiger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接