Java 为什么select()在我的程序中占用了这么多CPU时间?

Java 为什么select()在我的程序中占用了这么多CPU时间?,java,profiling,selector,mina,Java,Profiling,Selector,Mina,我有几个使用MINA的Java应用程序,它们都使用20个MINA线程。一个应用程序提供大约10000个并发连接,这些连接通常处于空闲状态,但有时会接收输入。20可能是该应用程序的一个合理线程数,尽管我还没有对其进行详细分析(这个问题就是针对这个问题)。另一个应用程序一次只提供大约15个连接,但会启动IO工作,因此它们非常繁忙,并且有20个MINA线程,这显然太多了 令我感到奇怪的是,这两个应用程序总是将大约30%的CPU时间(有时高达60%)投入MINA的select()方法(在VisualVM

我有几个使用MINA的Java应用程序,它们都使用20个MINA线程。一个应用程序提供大约10000个并发连接,这些连接通常处于空闲状态,但有时会接收输入。20可能是该应用程序的一个合理线程数,尽管我还没有对其进行详细分析(这个问题就是针对这个问题)。另一个应用程序一次只提供大约15个连接,但会启动IO工作,因此它们非常繁忙,并且有20个MINA线程,这显然太多了

令我感到奇怪的是,这两个应用程序总是将大约30%的CPU时间(有时高达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.lang.Thread.State:可运行
位于sun.nio.ch.EPollArrayWrapper.epollWait(本机方法)
在sun.nio.ch.EPollArrayWrapper.poll上(EPollArrayWrapper.java:228)
位于sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:81)
在sun.nio.ch.SelectorImpl.lock和doselect上(SelectorImpl.java:87)
-锁定(sun.nio.ch.Util$2)
-锁定(java.util.Collections$UnmodifiableSet)
-锁定(sun.nio.ch.EPollSelectorImpl)
在sun.nio.ch.SelectorImpl.select上(SelectorImpl.java:98)
位于org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:72)
位于org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1093)
位于org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
位于java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
位于java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
运行(Thread.java:722)
这似乎是基于一个繁忙的民意调查,这听起来真的不对

当我看到这么高的数字时,我应该担心吗?这是什么原因?这是我需要优化的东西,还是更类似于睡眠或空闲的例行程序?如果它更像是一个睡眠例程,它的优先级是否会比其他CPU工作的优先级低

更新:似乎是相同的问题。我听从了它的建议,现在正在运行Java 1.7.045,但我仍然看到在一个10k连接的应用程序中,
select
占用了高达90%的CPU时间


我们正在使用MINA 2.0.4,这意味着它是固定的。

一个应用程序正在轮询10000个连接,每个连接只使用很少的CPU,但所有这些加起来可能占CPU时间的很大一部分。 所有的优先事项都是让其他工作先完成


另一个连接数较少但每次连接处理次数较多的应用程序也可能显示较高的百分比,但它应该显示较低的等待时间和较高的CPU占用率。

与前面提到的链接问题的答案一样,常见的问题是较旧的JDK bug。由于您现在使用的是更新版本,我认为这实际上可能是一个硬件瓶颈

这里有一个指向glassfish问题的链接,描述了他们在哪里讨论硬件(网络和服务器)成为问题根源的可能性

另外,这里还有一个类似的问题,还没有答案:

首先,两个应用程序都有相同的问题,这很好;这可能表明问题出在JVM或操作系统上,而不是应用程序上:-)

正如jzd所提到的,
nio.select()
有问题。{Java的各种版本}x{各种平台、内核版本}的倍增使得它看起来像是一个到处都是的问题。我希望这些作品中有一部适合您:

  • 如果您在Linux上,请尝试
    2.6
    内核,以防您在
    2.4

    ,假设该错误类似于:

  • 使用较旧的JRE/JDK版本,而不是最新版本

    ,即返回JRE 6/JDK 6而不是7

试一试

  • {较旧版本的JRE(6)、较旧版本的Linux内核}或
  • {较新版本的JRE(7),较新版本的Linux内核}

而不是像{older,newer}或{newer,older}那样把它们混在一起。

不幸的是,这是对数字的错误解释

我已经多次面对这种情况(并且也提出了一个问题)

主要原因是VisualVM没有显示正确的CPU时间。它显示处于
运行
状态的线程时间百分比。但是从
Thread.State
上的文档中:

可运行线程的线程状态。可运行的线程 状态正在Java虚拟机中执行,但可能 正在等待来自操作系统的其他资源 例如处理器

这就是正在发生的事情。实际上,线程在OS
epoll\u wait()调用中被阻塞。在LinuxBox上,有几种方法可以确认这种情况

strace
'ing线程 线程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)
[...]
在这种情况下,线程id为
0xe12f
(应转换为十进制)。您将看到线程大部分时间都在
epoll\u wait()
call中

pidstat
ing线程 您将看到该线程的系统和用户CPU时间都很低,这意味着它不消耗CPU

使用
ps
您将看到大多数时间线程处于
S
Sl
(可中断睡眠)状态,而不是
R
(可运行)


最后,如果服务没有运行问题,您不必担心这一点。

您使用的是哪一版本的MINA?也许是NioProcessor上的一个bug,@vzamanillo我们在2.0.4上;它说2.0.3中的修复可能与2.0.5中解决的另一个错误有关,在内核版本3.2上。听起来不错,但下一个问题是如何配置CPU。(仅供参考,我不再处理这段代码了,因此无法跟踪某些东西是否有效。)VisualVM将和Java Flight Recorder一样出色。但仅适用于CPU边界
$ 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)
[...]
$ pidstat -tu -p [java-pid] | grep [thread pid]
$ ps -eL -o pid,tid,state | grep [thread-id]