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

Почему select() потребляет столько процессорного времени в моей программе?

У меня есть несколько приложений Java, которые используют MINA, и все они используют 20 потоков MINA. Одно приложение обслуживает около 10 000 одновременных подключений, которые обычно неактивны, но иногда принимают вход. 20, вероятно, является разумной встречей для этого приложения, хотя я точно не профилировал его (к чему этот вопрос идет). Другое приложение обслуживает только около 15 подключений одновременно, но инициирует работу ввода-вывода, поэтому они очень заняты и имеют 20 потоков MINA, что явно слишком много.

То, что странно для меня, - оба приложения всегда выделяют около 30%, иногда до 60%, своего времени процессора в метод MINA select(), профилированный в VisualVM. Стек вызова выглядит следующим образом:

java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:228)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:81)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
- locked <40ca5d54> (a sun.nio.ch.Util$2)
- locked <24649fe8> (a java.util.Collections$UnmodifiableSet)
- locked <3fae9662> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
at org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:72)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1093)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)

Кажется, что он основан на занятом опросе, который звучит для меня совершенно неправильно.

Должен ли я беспокоиться, когда я вижу, что это высоко? Что вызывает это? Это что-то, что мне нужно для оптимизации или это более похоже на спящий или простой режим? Если он больше похож на режим сна, он как-то запланирован на более низкий приоритет, чем работа другого процессора?

Обновление: этот поток, похоже, та же проблема. Я выполнил его рекомендации, и теперь я запускаю Java 1.7.0_45, но я все еще вижу, что select занимает до 90% времени процессора в приложении с подключением 10 тыс..

Мы используем MINA 2.0.4, что означает исправлена ​​эта соответствующая ошибка.

4b9b3361

Ответ 1

К сожалению, это неправильная интерпретация чисел.

Я столкнулся с такой ситуацией много раз (и задаю вопрос о fooobar.com/questions/218800/...).

Основная причина заключается в том, что VisualVM не показывает правильное время процессора. Он показывает процент времени потока в состоянии RUNNING. Но из документации по Thread.State:

Состояние резьбы для исполняемого потока. Поток в runnable выполняется на виртуальной машине Java, но может ждать других ресурсов из операционной системы таких как процессор.

Это именно то, что происходит. Фактически, поток блокируется внутри вызова OS epoll_wait(). В поле Linux есть несколько способов подтвердить это.

strace 'ing thread

$ strace -tttT -f -p [thread-id]

Идентификатор потока можно получить из вывода jstack:

$ jstack [java-pid]
[...]
"Netty Builtin Server 1" #17 prio=5 os_prio=31 tid=0x00000001013dd800 nid=0xe12f runnable [0x0000700001fe4000]
  java.lang.Thread.State: RUNNABLE
  at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
  at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)
[...]

в этом случае идентификатор потока 0xe12f (должен быть преобразован в десятичный). Вы увидите, что большая часть потока времени будет в вызове epoll_wait().

pidstat ing thread

$ pidstat -tu -p [java-pid] | grep [thread pid]

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

состояние опроса с использованием ps

$ ps -eL -o pid,tid,state | grep [thread-id]

вы увидите большую часть времени в состоянии S или Sl (прерывистый сон) вместо R (runnable).

В конце вы не должны беспокоиться об этом, если в службе нет никаких проблем.

Ответ 2

Одно приложение - опрос 10 000 подключений, с использованием очень небольшого количества ЦП на одно соединение, но в совокупности это может привести к значительной доле процессорного времени. Все приоритеты заключаются в том, что некоторые другие задания сначала попадают в строку.

В другом приложении с меньшим количеством подключений, но более хрустном на одном соединении может также отображаться более высокий процент, но он должен показывать меньшую долю времени ожидания и более высокую долю CPU.

Ответ 3

Как и ответ из связанного вопроса, общая проблема - это старые ошибки JDK. Поскольку теперь вы находитесь на обновленной версии, я думаю, что это может быть аппаратное узкое место.

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

https://www.java.net//forum/topic/glassfish/glassfish/glassfish-31-deadlock-epollarraywrapperepollwait-how-handle

Кроме того, вот еще один подобный вопрос без ответа: SelectorImpl заблокирован

Ответ 4

Во-первых, хорошо, что оба приложения имеют одинаковую проблему; это, вероятно, указывает на то, что проблема связана с JVM или ОС, а не с вашим приложением: -)

Как упоминалось в jzd, nio.select() возникли проблемы. Умножение {различных версий Java} x {различных платформ, версий ядра} заставляет его казаться все более актуальной проблемой. Я надеюсь, что одно из этих работ для вас:

  • Если вы работаете в Linux, попробуйте ядро ​​ 2.6, если вы находитесь на 2.4

    предполагая, что ошибка сродни: http://bugs.sun.com/view_bug.do?bug_id=6670302

  • Используйте версию старше JRE/JDK, а не последнюю версию!

    т.е. вернуться к JRE 6/JDK 6 вместо 7.

Try

  • {старая версия JRE (6), более ранняя версия ядра Linux} или
  • {новая версия JRE (7), более новая версия ядра Linux}

вместо того, чтобы смешивать их, как в {старше, новее} или {более новых, более старых}.