HttpClient 内存管理

我有一个应用程序,它有一个线程池(ThreadPoolExecutor),它被赋予任务,每个任务执行一个HttpGet操作,并将InputStream读入一个字节[]来做一些事情。

在阅读了 HttpClient 文档之后,我得出的印象是,跨多个线程管理 HttpClient 连接的最佳方法是创建单个 ThreadSafeClientConnManager 并通过应用程序共享它。

在实现这一点之后,我注意到即使在所有任务完成后,ThreadSafeClientConnManager仍然使用大量的内存。

查看堆转储,此内存采用 byte[] 数组的形式。这些不被我创建的任何参考资料所持有。它们被 ThreadSafeClientConnManager 及其池中的一部分所持有。我不确定它们是否与输入流有关,或者它们是其他东西。

所有任务本身及其变量都已成功回收垃圾回收。

如果我在 ThreadSafeClientConnManager 上调用 getConnectionManager().shutdown(),那么所有的内存都可以被释放。但是,我不想关闭连接,因为这些HttpGet任务可能随时发生。我想在应用程序生命周期内将其保持打开状态。

随着 HttpGet 任务的运行,所持有的内存会越来越多,最终可能导致内存不足错误。任务完成后,不会释放内存。

如何确保在使用内存的任务完成后释放内存?

这是我正在使用的代码。它是按照我从HttpClient文档中编写的代码,SO和在线上的其他问题拼凑在一起的。

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一起使用?

非常感谢。


答案 1

你有没有调用过 ClientConnectionManager 的 releaseConnection(...) 或 closeExpiredConnections() 方法?


答案 2

在最后的块中添加对 HttpEntity.consumeContent() 的调用


推荐