2011-02-15 270 views
13

我有一個應用程序有一個線程池(ThreadPoolExecutor)是交給任務,每個任務執行HttpGet操作並將InputStream讀入一個字節[]以執行某些操作。HttpClient內存管理

在閱讀了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是非常準確基於從HttpClient examplesManual connection release的例子進行了Runnable。下面是它的一個例子:

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?

非常感謝。

+0

我面臨類似問題的種類與HttpClient的4.3.6。我在每個請求完成後釋放連接,檢查陳舊連接,刪除空閒連接和過期連接,在讀取響應實體後關閉輸入流。但PoolingHttpclientConnectionManager仍然以某些會話輸入/輸出緩衝區的形式保存字節數組。你是如何解決這個問題的?任何幫助將不勝感激 – 2015-07-25 06:17:16

回答

6

你曾經調用ClientConnectionManager的releaseConnection(...)或closeExpiredConnections()方法嗎?

+0

沒有。我會在哪裏或如何實施? – cottonBallPaws 2011-02-15 04:53:34

+1

closeExpiredConnections()本身並不適合我,但是closeIdleConnections()(它也調用closeExpiredConnections())。似乎到目前爲止工作。感謝您指點我正確的方向。 – cottonBallPaws 2011-02-16 02:24:28

1

HttpClient 4.0和4.1中沒有存儲管理的已知問題。

您使用的是什麼版本的HttpClient,以及JRE是什麼?