Управление памятью HttpClient - PullRequest
14 голосов
/ 15 февраля 2011

У меня есть приложение, которое имеет пул потоков (ThreadPoolExecutor), который обрабатывает задачи, каждая из которых выполняет операцию HttpGet и считывает InputStream в байт [], чтобы что-то делать с ним.

После прочтения документации по HttpClient у меня сложилось впечатление, что лучший способ управлять соединениями HttpClient между несколькими потоками - это создать один ThreadSafeClientConnManager и поделиться им через приложение.

После реализации этого я заметил, что даже после того, как все задачи завершены, ThreadSafeClientConnManager все еще использует значительный объем памяти.

Глядя на дамп кучи, эта память представлена ​​в виде байтовых [] массивов. Они не содержатся ни в каких ссылках, которые я создал. Они удерживаются частями ThreadSafeClientConnManager и его пула. Я не уверен, относятся ли они к InputStreams или являются чем-то другим.

Все задачи и их переменные успешно собраны в мусор.

Если я вызываю getConnectionManager (). Shutdown () в ThreadSafeClientConnManager, тогда вся память освобождается просто отлично. Однако я не хочу закрывать соединение, потому что эти задачи 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);

Тогда Runnable, который выполняет HttpGet, в значительной степени основан именно на примере из примеров 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?

Большое спасибо.

Ответы [ 3 ]

7 голосов
/ 15 февраля 2011

Вы когда-нибудь вызывали методы releaseConnection (...) или closeExpiredConnections () ClientConnectionManager?

5 голосов
/ 15 февраля 2011

В блоке finally добавьте вызов HttpEntity.consumeContent ()

1 голос
/ 15 февраля 2011

Нет известных проблем с управлением памятью в HttpClient 4.0 и 4.1.

Какую версию HttpClient вы используете и что такое JRE?

...