为什么select()在我的程序中消耗了这么多CPU时间?

我有几个使用MINA的Java应用程序,它们都使用20个MINA线程。 一个应用程序为大约10,000个并发连接提供服 20可能是该应用程序的一个合理的线程数,虽然我还没有完全描述它(这个问题正在进行中)。 另一个应用程序一次只能提供大约15个连接,但启动IO工作,因此它们非常繁忙,无论如何都有20个MINA线程,这显然太多了。

对我来说很奇怪的是,两个应用程序总是把他们的CPU时间的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  (a sun.nio.ch.Util$2) - locked  (a java.util.Collections$UnmodifiableSet) - locked  (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) 

它似乎是基于繁忙的民意调查,这对我来说听起来真的不对。

当我看到那么高的数字时,我应该担心吗? 是什么导致这个? 这是我需要优化的东西还是更类似于睡眠或闲置程序? 如果它更像是一个睡眠例程,它会以某种方式被安排为比其他CPU工作更低的优先级?

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

我们正在使用MINA 2.0.4,这意味着修复了相关的错误 。

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

我多次面对这种情况(并在stackoverflow上问一个问题)。

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

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

这正是正在发生的事情。 实际上,在OS epoll_wait()调用中阻塞了线程。 在Linux框中,有几种方法可以确认是这种情况。

strace

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

线程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_wait()调用中的大部分时间。

pidstat ing thread

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

你会看到这个线程的低系统和用户CPU时间,这意味着它不消耗CPU。

使用ps轮询线程状态

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

你将看到大部分时间线程处于状态SSl (可中断睡眠)而不是R (可运行)。

如果服务没有运营问题,最后你不应该担心。

一个应用程序轮询10,000个连接,每个连接使用非常少的CPU,但总之,它可以累计很多CPU时间。 所有优先事项都是让其他工作先排好。

另一个具有较少连接但每个连接有更多紧缩的应用程序也可以显示更高的百分比,但它应该显示较低的等待时间,以及较高的CPU分数。

就像所提到的链接问题的答案一样,常见的问题是较旧的JDK错误。 由于您现在处于更新版本,我认为这实际上可能是硬件瓶颈。

这是一个玻璃鱼问题的链接,描述了他们讨论硬件(网络和服务器)成为问题根源的可能性。

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

此外,这是另一个类似的问题,还没有答案: SelectorImpl是BLOCKED

首先,两个应用程序都有相同的问题是好的; 它可能表明问题出在JVM或OS上,而不是你的应用程序:-)

正如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。

尝试

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

而不是像{较旧,较新}或{较新,较旧}那样混合它们。