NIO的epoll空轮询bug - Lost blog - 博客园 https://www.cnblogs.com/JAYIT/p/8241634.html

NIO的epoll空轮询bug

 

JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而已,它并没有被根本解决。该BUG以及与该BUG相关的问题单可以参见以下链接内容。

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6403933

参考:https://github.com/netty/netty/issues/327

参考:https://www.jianshu.com/p/d0f06b13e2fb

参考:http://blog.jobbole.com/105564/

参考:http://blog.csdn.net/xyls12345/article/details/26571699

Selector BUG出现的原因

若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%,

Netty的解决办法

  • 对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,

  • 若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。

  • 重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。

参考:http://blog.csdn.net/baiye_xing/article/details/73351330

前面讲到了epoll的一些机制,与select和poll等传统古老的IO多路复用机制的一些区别,这些区别实质可以总结为一句话,

就是epoll将重要的基于事件的fd集合放在了内核中来完成,因为内核是高效的,所以很多关于fd事件监听集合的操作也是高效的,

不方便的就是,因为在内核中,所以我们需要通过系统调用来调用关于fd操作集合,而不是直接自己攒一个。

如果在linux中,epoll在JDK6中还需要配置,在后续的版本中为JDK的NIO提供了默认的实现,但是epoll在JDK中的实现却是漏洞百出的,

bug非常的多,比较容易复现并且被众多人诟病的就是epoll轮询的处理方法。

sun的bug列表为:

JDK-6670302 (se) NIO selector wakes up with 0 selected keys infinitely [lnx 2.4]

JDK-6670302 : (se) NIO selector wakes up with 0 selected keys infinitely [lnx 2.4]

===》这个bug的描述内容为,在NIO的selector中,即使是关注的select轮询事件的key为0的话,NIO照样不断的从select本应该阻塞的

情况中wake up出来,也就是下图中的红色阻塞的部分:

然后,因为selector的select方法,返回numKeys是0,所以下面本应该对key值进行遍历的事件处理根本执行不了,又回到最上面的while(true)循环,循环往复,不断的轮询,直到linux系统出现100%的CPU情况,其它执行任务干不了活,

最终导致程序崩溃。

==》从这个bug上来看,这个绝对是JDK中的问题,select方法就应该是阻塞的,没有key事件过来,那么就不应该返回,和应用程序的写法没有任何的关系,与之相差不多的一个bug给出了解决的方案:

JDK-6403933 (se) Selector doesn't block on Selector.select(timeout) (lnx)

JDK-6403933 : (se) Selector doesn't block on Selector.select(timeout) (lnx)

这个bug的意思基本上和前面的JDK-6670302相差不大,也是Selector不阻塞,前一个bug说明的是最终的现象,

这个JDK-6403933的bug说出了实质的原因:

具体解释为,在部分Linux的2.6的kernel中,poll和epoll对于突然中断的连接socket会对返回的eventSet事件集合置为POLLHUP,也可能是POLLERR,eventSet事件集合发生了变化,这就可能导致Selector会被唤醒。==》这是与操作系统机制有关系的,JDK虽然仅仅

是一个兼容各个操作系统平台的软件,但很遗憾在JDK5和JDK6最初的版本中(严格意义上来将,JDK部分版本都是),这个问题并没有解决,而将这个帽子抛给了操作系统方,这也就是这个bug最终一直到2013年才最终修复的原因,最终影响力太广。

修复的方法,在这个bug中已经提到了:

上面是第一个建议,首先将SelectKey去除掉,然后“刷新”一下Selector,刷新的方式也就是调用Selector.selectNow方法,

这个示意的代码如下:

这段代码意味着重置,首先将SelectionKey注销掉,然后重新调用非阻塞的selectNow来让Selector换取“新生”。

这种修改方式就是grizzly的commiteer们最先进行修改的,并且通过众多的测试说明这种修改方式大大降低了JDK NIO的问题。

但是,这种修改仍然不是可靠的,一共有两点:

1.多个线程中的SelectionKey的key的cancel,很可能和下面的Selector.selectNow同时并发,如果是导致key的cancel后运行很可能没有效果

2.与其说第一点使得NIO空转出现的几率大大降低,经过Jetty服务器的测试报告发现,这种重复利用Selector并清空SelectionKey的改法很可能没有任何的效果,

最终的终极办法是创建一个新的Selector:

具体的Jetty服务器的分析地址为:

Jetty/Feature/JVM NIO Bug

Jetty首先定义两了-D参数:

  • org.mortbay.io.nio.JVMBUG_THRESHHOLD, defaults to 512 and is the number of zero select returns that must be exceeded in a period.

  • org.mortbay.io.nio.MONITOR_PERIOD defaults to 1000 and is the period over which the threshhold applies.

第一个参数是select返回值为0的计数,第二个是多长时间,整体意思就是控制在多长时间内,如果Selector.select不断返回0,说明进入了JVM的bug的模式

那么,Jetty这时候就有所作为了,我们看到Jetty的具体的代码如下:

首先,根据-D参数判断是否进入了JAVA NIO空转的bug模式,一个是判断时间,一个是判断次数,次数通过-jvmBug作为计数器进行统计;如果一旦确定是bug,可以看到上述代码为了防止并发出现,加了Sychronized锁,接着开启一个新的Selector,并将原有的SelectionKey的事件全部转移到了新的Selector中,最后将-jvmBug计数器置0;

==》这种处理方法要保险的多,基本上不会有任何的问题了,

Jetty在这个网页中还提供了很多参数,如:

即使上述的处理方式,对应极少的linux环境和JDK的版本,仍会出现一些问题,这主要是因为网络中断的间隔时间太短造成的,需要给内核一定的时钟周期进行缓冲,而上述的Jetty的org.mortbay.io.nio.BUSY_PAUSE这个参数就是起到间隔的作用,间隔多少微秒再调用Select,这样基本上能最大程度上避免上述问题出现了。

从上面Jetty各种处理方法来看,基本能屏蔽低版本JDK和操作系统的epoll的影响,让NIO可以无忧运行。当然,对于NIO框架也是修正了这些错误,前面提到的Griizzly和Netty都对这个问题采取了响应的策略。

以Netty为例,具体位置在NioSelector的实现类AbsNioSelector中:

上述的思路和Jetty的处理方式几乎是一样的,就是netty讲重建Selector的过程抽取成了一个方法,叫做rebuildSelector,可以看看其方法:

基本上类似,这里就不再缀余。

分析到这里,可以看到为什么NIO框架如Netty,Grizzly,还有最近的炒得很热的Jboss的UnderTow,NIO远远不止这篇文章分析得这一个,还有很多,大可在JDK官网上去查,而这些框架都将NIO的很多不好用的问题,bug隐藏起来了,并加上诸如限流,字符转换,基于设计模式等特性,让开发人员更好的编写高并发的程序,而不用过多的网络的关注与细节。

由此可见,现在JAVA真是越来越危机了,从前几年的SSH把java ee给替换掉,到现在jdk都时不时冒出一个bug来,而且最近JDK8中的一个bug大有超过这个bug之势,jcp社区确实需要好好反省了,要不然java没落了,一干程序员又得下岗再就业了。

总结:

NIO的空转bug历史悠久流传广泛,应用服务器的前端框架一般都采取换一个新Selector的方式对此进行处理,屏蔽掉了JDK5/6的问题,但对于此问题来讲,还是尽量将JDK的版本更新到最新,或者使用NIO框架如Netty,Grizzly等进行研发,以免出更多的问题。

epoll bug CPU空轮询

SUN在解决该BUG的问题上不给力,只能从NIO框架层面进行问题规避,下面我们看下Netty是如何解决该问题的。

Netty的解决策略:

1) 根据该BUG的特征,首先侦测该BUG是否发生;

2) 将问题Selector上注册的Channel转移到新建的Selector上;

3) 老的问题Selector关闭,使用新建的Selector替换。

下面具体看下代码,首先检测是否发生了该BUG:

图2-27 epoll bug 检测

一旦检测发生该BUG,则重建Selector,代码如下:

图2-28 重建Selector

重建完成之后,替换老的Selector,代码如下:

图2-29 替换Selector

大量生产系统的运行表明,Netty的规避策略可以解决epoll bug 导致的IO线程CPU死循环问题。

netty的解决代码在package io.netty.channel.nio.nioEventLoop这个类下面。

 
 
 

DK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。的更多相关文章

  1. NIO的epoll空轮询bug

    JDK NIO的bug,例如epoll bug,它会导致Selector空轮询,最终导致CPU 100%. Selector BUG出现的原因 若Selector的轮询结果为空,也没有wakeup或新 ...

  2. jdk1.6空轮询Bug的原因及解决方法

    简述 本文主要介绍一下jdk1.6版本中的NIO Selector空轮询BUG,描述一下BUG的现象及原因,以及Netty中如何巧妙的规避了这个bug. 为什么要写这篇文章,说来惭愧,很久以前面试官问 ...

  3. Java nio 空轮询bug到底是什么

    编者注:Java nio 空轮询bug也就是Java nio在Linux系统下的epoll空轮询问题. epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效 ...

  4. bug生命周期和bug状态处理

    首先,测试人员发现 BUG ,做好记录并上报至 BUG 数据库.接着,开发组长或经理确定该 BUG 是否有效 之后指定 BUG 的优先级并安排给相关开发人员.否则拒绝该 BUG 的修复. 然后,该 B ...

  5. jquery下ie的margin-left ----bug 以及parseInt方法bug

    ie下使用jquery的方法css('margin-left')可能会出现'auto'----从而使结果不可计算,即便使用parseInt()方法也不行 因为parseInt()方法的bug是如果参数 ...

  6. IE9的BUG?jQuery的BUG?

    本文转载自http://big-student.iteye.com/blog/1898213 在IE9和IE10中,当对一个html的样式初始了一个很大的left或者top时,使用jQuery的off ...

  7. IE6条件下的bug与常见的bug及其解决方法

    1.IE6条件下有双倍的margin 解决办法:给这个浮动元素增加display:inline属性 2. 图片底部有3像素问题 解决办法:display:block;或者vertical-align: ...

  8. 轮播图的3个常见bug,即处理bug思路及其解决办法

    1,下载jquery.js文件,并且导入 2,在下面的img中写入可以用图片路径 <!-- 第一个bug: 刚打开页面时,按一下左键图片没切换,再按第二下时才切换图片. 第二个bug: Ctrl ...

  9. python之Bug之字符串拼接bug

    \r\n拼接Bug 环境: python3.4.pycharm2017 偶然的学习中遇到了一个问题,百思不得姐,什么问题呢,大家输入太快了,难免有失误就如下面的代码 #构造响应数据 response_ ...

随机推荐

  1. CXAnimation.h动画类

    /**************************************************************************** 使用一个CCAnimation对象可以CCS ...

  2. Mac OS下配置PHP Nginx PHP-FPM

    首先需要安装homebrew, 不赘述了 php-fpm php-fpm是mac下自带的软件, 而且兼容不同的PHP版本, 不用额外安装, 但是fpm是需要配置的, 在/private/etc下有个模 ...

  3. 0068 Git入门的第一节课

    这是 猴子都懂的Git入门 的学习笔记 Git安装与配置 下载安装Git:http://git-scm.com/ 从开始菜单启动Git Bash $ git --version git version ...

  4. Unix系统编程()在文件特定偏移量处的IO:pread和pwrite

    首先我想问的是这两个p代表的是什么? 系统调用pread和pwrite完成与read和write相类似的工作,只是前两者会在offset参数所指定的位置进行文件IO操作,而非始于文件的当前偏移量处,并 ...

  5. nginx的root alias 指令

    location /img/ { alias /var/www/image/; } #若按照上述配置的话,则访问/img/目录里面的文件时,ningx会自动去/var/www/image/目录找文件 ...

  6. 阿里云ecs开启x11图形化桌面

    阿里云帮助文档:https://www.alibabacloud.com/help/zh/faq-detail/41227.htm 安装云服务器 ECS CentOS 7 图形化桌面 以安装 MATE ...

  7. 关于Web应用程序,下列说法错误的是( )。

    关于Web应用程序,下列说法错误的是( ). A.WEB-INF目录存在于web应用的根目录下 B. WEB-INF目录与classes 目录平行 C. web.xml在WEB-INF目录下 D. W ...

  8. php -- 魔术方法 之 删除属性:__unset()

    属性重载:当访问一个不存在或者权限不够的属性的时候,能够触发一系列的魔术方法,就叫做属性重载 __unset():当删除一个不存在或者权限不够的属性的时候会自动触发 <?php //属性重载 c ...

  9. php -- 魔术方法 之 对象克隆 : __clone()

    产生对象的方法: 1.从类产生对象.new,通过new去堆区开辟一块内存空间 2.从对象产生对象.clone,通过关键字clone,将一个对象完整的复制一份,新开辟一块内存空间,将复制的结果存放到该内 ...

  10. 【BZOJ】1028: [JSOI2007]麻将(贪心+暴力)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1028 表示不会贪心QAQ 按顺序枚举,如果能形成刻子那么就形成刻子,否则形成顺子.orz 证明?:因 ...