HttpClient内存管理
我有一个应用程序,它有一个线程池(ThreadPoolExecutor),它负责分配任务,每个任务执行 HttpGet 操作并将 InputStream 读入 byte[] 中以执行某些操作。
阅读 HttpClient 文档后,我得到的印象是,跨多个线程管理 HttpClient 连接的最佳方法是创建一个 ThreadSafeClientConnManager 并在整个应用程序中共享它。
实现此操作后,我注意到即使所有任务都完成后,ThreadSafeClientConnManager 仍然使用大量内存。
查看堆转储,该内存采用 byte[] 数组的形式。我创建的任何参考文献都没有保存这些内容。它们由 ThreadSafeClientConnManager 及其池的各个部分保存。我不确定它们是否与 InputStreams 有关或者它们是否是其他东西。
所有任务本身及其变量都已成功进行垃圾收集。
如果我在 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 一起使用?
非常感谢。
I have an application that has a thread pool (ThreadPoolExecutor) that is handed tasks that each perform a HttpGet operation and read the InputStream into a byte[] to do something with.
After reading the HttpClient docs I came away with the impression that the best way to manage HttpClient connections across multiple threads is to create a single ThreadSafeClientConnManager and share it through out the application.
After implementing this, I am noticing that even after all of the tasks are completed there is still a significant amount of memory still being used by the ThreadSafeClientConnManager.
Looking at the heap dump, this memory is in the form of byte[] arrays. These are not being held by any references that I created. They are being held by pieces of the ThreadSafeClientConnManager and its pool. I am not sure if they are related to the InputStreams or if they are something else.
All of the tasks themselves and their variables are successfully garbage collected.
If I call getConnectionManager().shutdown() on the ThreadSafeClientConnManager then all of the memory is freed just fine. However, I do not want to have to shutdown the connection, because these HttpGet tasks could happen at anytime. I would like to leave it open during the duration of the applications life.
As the HttpGet tasks run, the memory being held grows more and more and can eventually lead to out of memory errors. When the tasks complete, the memory is not released.
How can I ensure the memory is released after the task that was using it is finished?
Here is the code I am using. It is pieced together as best as I code from the HttpClient docs, other questions here on SO and online.
The creation of the 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);
Then, the Runnable that performs the HttpGet is pretty much based exactly on the example from the HttpClient examples for Manual connection release. Here is an example of what it looks like:
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) {}
}
}
}
Is there more you have to do to release the resources per task? I saw in their ThreadSafeClientConnManager example they used a HttpContext, but I can't find any documentation on how to use it. Is that required? If so how do you use it with a ThreadPoolExecutor?
Thanks so much.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您是否调用过 ClientConnectionManager 的 releaseConnection(...) 或 closeExpiredConnections() 方法?
Do you ever invoke ClientConnectionManager's releaseConnection(...) or closeExpiredConnections() methods?
在finally块中添加对 HttpEntity.consumeContent 的调用()
In the finally block add a call to HttpEntity.consumeContent()
HttpClient 4.0 和 4.1 中没有已知的内存管理问题。
您使用什么版本的 HttpClient?JRE 是什么?
There are no known issues with memory management in HttpClient 4.0 and 4.1.
What version of HttpClient are you using and what is the JRE?