HttpClient内存管理

14
我有一个应用程序,它具有一个线程池(ThreadPoolExecutor),该线程池分配任务,每个任务执行一个 HttpGet 操作,并将 InputStream 读入一个 byte[] 中以进行后续操作。
在阅读 HttpClient 文档后,我的印象是,在多个线程间管理 HttpClient 连接的最佳方法是创建一个单独的 ThreadSafeClientConnManager 并在整个应用程序中共享它。
实现此后,我注意到即使在所有任务完成后,ThreadSafeClientConnManager 仍然使用了大量内存。
查看堆转储(heap dump),这些内存以 byte[] 数组的形式存在。它们不由我创建的任何引用持有。它们由 ThreadSafeClientConnManager 及其池的部分持有。我不确定它们是否与 InputStream 相关,还是其他东西。
所有任务本身及其变量都已成功进行了垃圾回收。
如果我在 ThreadSafeClientConnManager 上调用 getConnectionManager().shutdown(),则所有内存都会被良好地释放。但是,我不想关闭连接,因为这些 HttpGet 任务可以随时发生。我希望在应用程序的生命周期内保持连接处于打开状态。
当 HttpGet 任务运行时,保持的内存越来越多,最终可能导致内存不足错误。当任务完成时,内存不会被释放。
如何确保在使用它的任务完成后释放内存?
这是我正在使用的代码。它是从 HttpClient 文档、其他 Stack Overflow 上的问题和在线资源中拼凑而来的。
创建 HttpClient 的代码:
// Create and initialize HTTP parameters
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 40 * 1000);
HttpConnectionParams.setSoTimeout(params, 40 * 1000);
ConnManagerParams.setMaxTotalConnections(params, 100);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

// Create and initialize scheme registry 
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register( new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));

// Create an HttpClient with the ThreadSafeClientConnManager.
// This connection manager must be used if more than one thread will
// be using the HttpClient.
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
mHttpClient = new DefaultHttpClient(cm, params);

然后,执行 HttpGet 的 Runnable 基本上完全基于HttpClient 示例中的手动释放连接示例。以下是其示例:

HttpClient httpclient = getTheSharedThreadSafeClientConnManager(); // Would return the mHttpClient from above
    try {
        HttpGet httpget = new HttpGet("http://www.apache.org/");

        // Execute HTTP request
        System.out.println("executing request " + httpget.getURI());
        HttpResponse response = httpclient.execute(httpget);

        System.out.println("----------------------------------------");
        System.out.println(response.getStatusLine());
        System.out.println("----------------------------------------");

        // Get hold of the response entity
        HttpEntity entity = response.getEntity();

        // If the response does not enclose an entity, there is no need
        // to bother about connection release
        if (entity != null) {
            InputStream instream = entity.getContent();
            try {
                instream.read();
                // do something useful with the response
            } catch (IOException ex) {
                // In case of an IOException the connection will be released
                // back to the connection manager automatically
                throw ex;
            } catch (RuntimeException ex) {
                // In case of an unexpected exception you may want to abort
                // the HTTP request in order to shut down the underlying
                // connection immediately.
                httpget.abort();
                throw ex;
            } finally {
                // Closing the input stream will trigger connection release
                try { instream.close(); } catch (Exception ignore) {}
            }
        }

    }

释放每个任务的资源需要执行更多操作吗?在他们的ThreadSafeClientConnManager示例中,我看到他们使用了HttpContext,但我找不到任何有关如何使用它的文档。那是否需要使用它?如果是这样,如何在ThreadPoolExecutor中使用它?

非常感谢。


我使用httpclient 4.3.6也遇到了类似的问题。每次请求完成后,我都会释放连接、检查过时的连接、删除空闲连接和过期连接,并在读取响应实体后关闭输入流。但是,PoolingHttpclientConnectionManager仍然以某些会话输入/输出缓冲区的形式保存字节数组。您是如何解决这个问题的?任何帮助都将不胜感激。 - Sumit Kumar
3个回答

7

你是否曾经调用过ClientConnectionManager的releaseConnection(...)或closeExpiredConnections()方法?


不行。我应该在哪里或如何实现它? - cottonBallPaws
1
closeExpiredConnections()本身对我没有起作用,但closeIdleConnections()(它也调用closeExpiredConnections())起了作用。到目前为止似乎工作正常。感谢您指引我正确的方向。 - cottonBallPaws
1
closeExpiredConnections() 和 closeIdleConnections() 都无法正常工作,我不得不调用 shutdown() 来释放所有内存。HttpClient client = new DefaultHttpClient(); try { ... 做一些事情 ... } catch { ... } finally { client.getConnectionManager().shutdown(); } - robotniko

5

1

HttpClient 4.0和4.1中没有已知的内存管理问题。

您使用的是哪个版本的HttpClient,以及JRE是什么版本?


Java Runtime Environment 是相对于 JDK(没有调试工具)的一个运行时环境。 - Bostone

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