Подтвердить что ты не робот

Причины использования большого количества процессоров в SocketInputStream.socketRead0()

Во время профилирования встроенного веб-приложения я натолкнулся на следующее очень странное (по крайней мере для меня) наблюдение.

Почти все время проведено в socketRead0() методе класса SocketInputStream. Это неудивительно, потому что мое приложение делает работу с удаленной службой по каждому запросу. Странно, что для этого метода не только использование времени настенных часов является слишком высоким, так как время процессора тоже очень велико. Я не могу понять, почему процессорное время велико, потому что, если мое приложение ожидает ответа на удаленный сервис (что на самом деле не так быстро), для самого приложения нечего делать. Поэтому время процессора должно быть явно низким.

Несколько наблюдений:

  • VisualVM в режиме выборки показывает, что метод SocketInputStream.socketRead0() едет до 95% времени (время работы настенных часов и);
  • mpstat (мы используем Linux как ОС) показывает около 90% времени пользователя и ~ 1-3% системного времени (остальное время простоя);
  • приложение, развернутое на выделенном сервере;
  • удаленная служба также является веб-приложением HTTP. Среднее время отклика составляет около 100 мс. Средний размер ответа составляет около 2 КБ.
  • мое приложение использует spring RestTemplate для непосредственного взаимодействия с удаленным сервисом, а не с SocketInputStream.

Пока у меня есть только одна идея - может быть, это накладные расходы на вызов встроенных методов в JVM (SocketInputStream.socketRead0() является родным)?

Как вы думаете? Есть ли другие причины для этого?

4b9b3361

Ответ 1

VisualVM показывает нагрузку не как абсолютное значение, а как относительное значение, поэтому это просто означает, что ваше приложение больше не потребляет процессор.

Я считаю, что вы должны настроить VisualVM, чтобы не просверлить эту глубину, и скорее посчитайте этот вызов метода как часть метода, который находится в вашем коде (или spring).

Я уже испытал такое поведение, но он не выглядел так, как будто он требовал какой-либо оптимизации. Веб-приложение просто должно читать данные из сокетов (т.е. HTTP-запрос, база данных, внутренние сетевые службы...), и нет помощи.

Ответ 2

Я столкнулся с той же проблемой. Мое приложение имеет очень высокое значение qps, и каждый запрос заставит меня отправлять несколько связанных вызовов, которые используют этот собственный api: socketRead0

Поэтому я решил сделать эксперимент. Я делаю mock-сервер с api sleep 30s перед возвратом, и клиент вызывает этот api. Моя цель - проверить статус потока при запуске сети. На основе моего дампа потока статус потока RUNNABLE.

Это объясняет две вещи:

  • приложение с высокой qps-блокировкой io столкнется с высоким значением загрузки процессора

  • ваш поток java по-прежнему работает в jvm, так как состояние потока RUNNABLE, что будет способствовать использованию высокоуровневого пространства cpu

оба из них сделают ваш процессор занятым.

Я заметил, что во время эксперимента использование системного пространства cpu низкое. Я думаю, что это связано с разницей стратегии планирования потоков между jvm и os. Мы знаем, что модель потоковой передачи hotspot составляет 1:1, что означает один поток jvm в один поток os. когда произошла блокировка io, например socketRead0, поток ядра установит состояние S и не будет блокировать процессор, но поток пользовательского пространства блокируется (ожидает). Когда это произошло, я думаю, нам нужно переосмыслить основную модель ввода-вывода в нашем приложении.