Утечка памяти Apache HttpAsyncClient

Мне нужно использовать HttpAsyncClient при высокой нагрузке. Я создаю HttpAsyncClient следующим образом:

RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(CONNECT_TIMEOUT)
        .setSocketTimeout(SOCKET_TIMEOUT)
        .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
        .build();
HttpAsyncClient client = HttpAsyncClients.custom()
        .setDefaultRequestConfig(createRequestConfig())
        .build();

И тогда я использую это так:

HttpPost request = new HttpPost(url);
request.setEntity(new StringEntity(requestBody, "UTF-8"));
client.execute(request, null)

Обычно меня не волнует ответ, поэтому я не инициализирую переменную Future<HttpResponse> и не делаю Future.get(). Ну, просто для уточнения (я не думаю, что это как-то связано с вопросом), мне иногда интересны ответы, но 99% ответов мне не интересны.

Проблема в том, что когда я делаю много запросов (например, 300 в секунду, и под словом «запрос» здесь я подразумеваю client.execute() вызов), я, наконец, получаю java.lang.OutOfMemoryError: GC overhead limit exceeded. Я попытался использовать VisualVM, чтобы узнать, что происходит. Я вижу, что количество экземпляров java.lang.Object[], char[], java.lang.String, byte[], short, char[] экземпляров растет (я пытался принудительно собрать сборщик мусора и ограничить размер кучи, чтобы убедиться, что это ненормально - не помогло). И поэтому растет используемое пространство кучи.

Что вызывает эту проблему? Может быть, мне следует использовать HttpAsyncClient как-то по-другому? Нужно ли использовать пользовательские RequestProducer, ResponseProducer или использовать CountDownLatch?

UPD Проблема возникла из-за библиотеки PowerMock


comment
Сколько HttpAsyncClient экземпляров у вас есть?   -  person Andremoniy    schedule 15.01.2016
comment
Я имею в виду, вы создаете client для каждого запроса или нет?   -  person Andremoniy    schedule 15.01.2016
comment
Нет, нет, у меня только один client   -  person coolguy    schedule 15.01.2016
comment
См. эту ссылку stackoverflow.com/questions/4775618/ Вы найдете решение в этой теме.   -  person eg04lt3r    schedule 15.01.2016
comment
Управление соединениями в классическом и асинхронном HC совершенно разное. Все, что было сказано в 4775618, не имеет отношения к HttpAsyncClient.   -  person ok2c    schedule 15.01.2016


Ответы (2)


HttpAsyncClient никоим образом не ограничивает скорость выполнения запросов, чтобы избежать блокировки метода #execute. Клиенту можно отправить неограниченное количество запросов, и клиент послушно поставит их все в очередь на выполнение. Насколько быстро эти запросы могут выполняться и удаляться из очереди — это совсем другая история, которая может зависеть от многих факторов. В вашем конкретном случае вы пытаетесь обработать все эти запросы всего с двумя одновременными соединениями на маршрут, что не обязательно является оптимальным.

PS: /ворчание старика/ люди не должны предполагать, что асинхронные клиенты будут почему-то быстрее. Они не будут использоваться, если они не используются для конкретных случаев использования и определенным образом. Несомненно, что можно легко в конечном итоге использовать больше памяти с асинхронными клиентами, если не быть очень осторожным.

person ok2c    schedule 15.01.2016
comment
не ограничивает скорость выполнения запросов каким-либо образом или способом, чтобы избежать блокировки метода #execute - не могли бы вы объяснить это немного подробнее? Я тоже думал об этой очереди запросов внутри клиента. Но вот в чем дело - я установил connectionRequestTimeout, поэтому я думал, что могу быть уверен, что если запрос в очереди не получит свое собственное соединение в течение 5 секунд, он будет отменен с помощью TimeoutException. Но почему-то похоже, что GC не может что-то почистить внутри клиента. - person coolguy; 15.01.2016
comment
Немного о производительности: в моем случае первое преимущество использования асинхронного клиента заключается в том, что поток не будет заблокирован; и во-вторых, возможно, поддержка конвейерной обработки. Конечно, я мог бы создать какой-то фасад над синхронным клиентом, но это было бы намного сложнее - поддержка отмены, некоторые другие особенности и т. д. - person coolguy; 15.01.2016
comment
Вместо того, чтобы гадать, вы должны выяснить, что именно не очищается. - person ok2c; 15.01.2016
comment
Любые идеи, как я могу это проверить? Я просмотрел исходный код HttpAsyncClient и не могу понять, что нельзя стереть. Насколько я понимаю, InternalHttpAsyncClient (это реализация, которую я использую в данный момент) создает новый DefaultClientExchangeHandlerImpl и запускает его. Он пытается получить соединение из пула, пока не истечет время ожидания. И там я не вижу никаких проблем, которые могли бы привести к устаревшим ссылкам или чему-то еще. Во всяком случае, похоже, что нет никакого API, с помощью которого я мог бы контролировать это снаружи. Правильно ли я использую HttpAsyncClient в своем вопросе? - person coolguy; 17.01.2016
comment
Я написал простой нагрузочный тест: pastebin.com/0XaquW35 . Вот часть вывода: pastebin.com/ykCyJNHY . Для меня было неожиданно, что ни один из запросов не был выполнен. Если я сплю 1000 мс на каждой итерации в моем тесте, все работает отлично. Странный способ HttpAsyncClient управлять запросами. Учитывая, что старые запросы удаляются из очереди на выполнение после тайм-аута connectionRequest, я не могу понять, есть ли шанс, что размер очереди постоянно растет. Кстати, под фразой execution queue вы имели в виду множество экземпляров DefaultClientExchangeHandlerImpl? - person coolguy; 17.01.2016
comment
Просто, например, мне нужен отзывчивый хост для этого примера теста. Не могли бы вы взглянуть на другие вопросы, пожалуйста? - person coolguy; 17.01.2016
comment
Почему? Какой смысл мне говорить вам, что нужно использовать профилировщик или анализатор памяти при расследовании потенциальной утечки памяти? - person ok2c; 17.01.2016
comment
Хорошо, попробую еще раз профилировать. Ну, насколько я понимаю, моя проблема в том, что HttpAsyncClient не может справиться со слишком большим количеством запросов и дросселей. Я только что нашел ваш ответ здесь stackoverflow.com/questions/30101865/ думаю, это то, что мне нужно, я попробую завтра - person coolguy; 17.01.2016
comment
HttpAsyncClient делает именно то, что указывает ваш код. Ваша проблема в том, что вы, кажется, не имеете четкого представления о том, что вы пытаетесь сделать. - person ok2c; 17.01.2016
comment
Давайте продолжим обсуждение в чате. - person coolguy; 18.01.2016

Причина заключалась в том, что я использовал PowerMock для запуска своих тестов. Без PowerMock все работает нормально.

person coolguy    schedule 21.01.2016