JDK NIO的bug,例如epoll bug,它会导致Selector空轮询,最终导致CPU 100%。
Selector BUG出现的原因
若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%
这个bug的描述内容为,在NIO的selector中,即使是关注的select轮询事件的key为0的话,NIO照样不断的从select本应该阻塞的
情况中wake up出来,也就是下图中的红色阻塞的部分:
然后,因为selector的select方法,返回numKeys是0,所以下面本应该对key值进行遍历的事件处理根本执行不了,又回到最上面的while(true)循环,循环往复,不断的轮询,直到linux系统出现100%的CPU情况,其它执行任务干不了活,最终导致程序崩溃。
从这个bug上来看,这个绝对是JDK中的问题,select方法就应该是阻塞的,没有key事件过来,那么就不应该返回,和应用程序的写法没有任何的关系
Netty的解决办法
1) 根据该BUG的特征,首先侦测该BUG是否发生
侦测方法:对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数;
若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug, netty默认是512次
2) 将问题Selector上注册的Channel转移到新建的Selector上;
3) 老的问题Selector关闭,使用新建的Selector替换。
下面具体看下代码,首先检测是否发生了该BUG:
epoll bug 检测:
一旦检测发生该BUG,则重建Selector,代码如下:
重建完成之后,替换老的Selector,代码如下:
大量生产系统的运行表明,Netty的规避策略可以解决epoll bug 导致的IO线程CPU死循环问题。
netty的解决代码在package io.netty.channel.nio.NioEventLoop这个类下面