2011-02-15 15 views
13

Tengo una aplicación que tiene un grupo de subprocesos (ThreadPoolExecutor) al que se le asignan tareas que realizan una operación HttpGet y leen el InputStream en un byte [] para hacer algo.HttpClient memory management

Después de leer los documentos de HttpClient, me salió la impresión de que la mejor manera de administrar las conexiones de HttpClient en varios subprocesos es crear un solo ThreadSafeClientConnManager y compartirlo a través de la aplicación.

Después de implementar esto, me doy cuenta de que, incluso después de que se completen todas las tareas, el ThreadSafeClientConnManager todavía tiene una cantidad significativa de memoria.

Al observar el volcado del montón, esta memoria tiene forma de matrices de bytes []. Estas no están retenidas por ninguna referencia que haya creado. Están retenidos por piezas del ThreadSafeClientConnManager y su grupo. No estoy seguro si están relacionados con InputStreams o si son algo más.

Todas las tareas en sí y sus variables se recogen correctamente.

Si llamo a getConnectionManager(). Shutdown() en el ThreadSafeClientConnManager, entonces toda la memoria se libera muy bien. Sin embargo, no quiero tener que cerrar la conexión, porque estas tareas de HttpGet podrían suceder en cualquier momento. Me gustaría dejarlo abierto durante la vida útil de las aplicaciones.

A medida que se ejecutan las tareas de HttpGet, la memoria retenida crece cada vez más y puede conducir a errores de falta de memoria. Cuando se completan las tareas, la memoria no se libera.

¿Cómo puedo asegurarme de que la memoria se libera después de que la tarea que la estaba utilizando haya finalizado?

Aquí está el código que estoy usando. Está estructurado de la mejor manera que codigo de los documentos HttpClient, otras preguntas aquí en SO y en línea.

La creación de la 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); 

Entonces, el Ejecutable que realiza la HttpGet se basan más o menos exactamente en el ejemplo de la HttpClient examples para Manual connection release. Aquí hay un ejemplo de cómo se ve:

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) {} 
      } 
     } 

    } 

¿Hay más que hacer para liberar los recursos por tarea? Vi en su ejemplo ThreadSafeClientConnManager que utilizaron un HttpContext, pero no puedo encontrar ninguna documentación sobre cómo usarlo. Es eso requerido? Si es así, ¿cómo lo usas con un ThreadPoolExecutor?

Muchas gracias.

+0

Estoy frente al mismo tipo de problema con httpclient 4.3.6.Estoy liberando la conexión después de completar cada solicitud, comprobando la conexión obsoleta, eliminando las conexiones inactivas y las conexiones caducadas, cerrando el flujo de entrada después de leer la entidad de respuesta. Pero aún el PoolingHttpclientConnectionManager está manteniendo arrays de bytes en forma de algunos buffers de entrada/salida de sesión. Cómo resolviste este problema? Cualquier ayuda será muy apreciada –

Respuesta

6

¿Alguna vez ha invocado los métodos releaseConnection (...) o closeExpiredConnections() de ClientConnectionManager?

+0

No. ¿Dónde o cómo implementaría eso? – cottonBallPaws

+1

closeExpiredConnections() en sí mismo no lo hizo por mí, pero closeIdleConnections() (que también llama a closeExpiredConnections()) lo hizo. Parece estar funcionando hasta ahora. Gracias por señalarme en la dirección correcta. – cottonBallPaws

+0

both closeExpiredConnections() y closeIdleConnections() no funcionó para mí. Tuve que llamar a shutdown() para liberar toda la memoria. HttpClient client = new DefaultHttpClient(); try { ... hacer algo ... } catch { ... } finally { client.getConnectionManager(). Shutdown(); } – robotniko

1

No hay problemas conocidos con la gestión de la memoria en HttpClient 4.0 y 4.1.

¿Qué versión de HttpClient estás usando y qué es JRE?

+0

Java Runtime Environment como opuesto a JDK (sin problemas de depuración) – Bostone