原文链接:应用服务器中对JDK的epoll空转bug的处理

前面讲到了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等进行研发,以免出更多的问题。

应用服务器中对JDK的epoll空转bug的处理的更多相关文章

  1. 在Linux中配置jdk,Tomcat,MySQL

    解压缩: tar 命令 : 使用方式 tar [参数] source [target] source - 压缩文件 target - 解压缩后的目标位置, 默认解压到当前目录 常用写法 : 解压缩 : ...

  2. 细分java环境中的JDK、JVM、JRE

    细分java环境中的JDK.JVM.JRE 近来小看了下Android,扑面而来一堆概念JDK.JVM.JRE.SDK.NDK.ADT.缕了一下,其中JDK.JVM.JRE是java环境的东西,而SD ...

  3. Linux虚拟机中配置JDK环境变量

    前提准备: 1,安装好Linux系统 2,下载好可以将文件传输到Linux系统工具例如:WinSCP 3,在windows中下载Linux版JDK: http://download.oracle.co ...

  4. Spring中的JDK动态代理

    Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层 ...

  5. C语言中史上最愚蠢的Bug

    C语言中史上最愚蠢的Bug   本文来自“The most stupid C bug ever”,很有意思,分享给大家.我相信这样的bug,就算你是高手你也会犯的.你来看看作者犯的这个Bug吧.. 首 ...

  6. 在tomcat中配置jdk的不同版本

    在tomcat中配置jdk的不同版本---------------------------------------------------------------------------------- ...

  7. IE6-9中tbody的innerHTML不能赋值bug

    IE6-IE9中tbody的innerHTML不能赋值,重现代码如下 <!DOCTYPE html> <html> <head> <meta charset= ...

  8. centos中更换jdk的版本

    现在讲的是Linux中更换jdk版本的问题,卸载Linux自带的jdk更换sun的jdk百度一大堆,但是如果我安装的sun的jdk是1.7的想更换到1.8的如何解决呢,方法其实超easy. 把1.8的 ...

  9. 在Eclipse中查看JDK类库的源代码

    http://blog.csdn.net/a81895898/article/details/8486802 在Eclipse中查看JDK类库的源代码!!! 设置: 1.点 “window”-> ...

随机推荐

  1. 使用Dom4解析xml

    XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便. XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. XML的解析方式分为 ...

  2. Java ConcurrentModificationException 异常分析与解决方案

    Java ConcurrentModificationException 异常分析与解决方案http://www.2cto.com/kf/201403/286536.html java.util.Co ...

  3. 解题:洛谷4314 CPU监控

    题面 线段树·二重标记(什么鬼 用(a,b)标记表示先执行+a操作,然后对b取max,维护历史/当前最大值和历史/当前标记.然后我们发现区间加$x$就是$(x,-inf)$,区间赋$x$就是$(-in ...

  4. 解题:USACO07FEB The Cow Lexicon

    题面 第一次做Trie上dp,感谢 @i207M 的资瓷 对子串们建立一棵Trie,设$dp[i][j]$表示到母串第$i$位为止在$Trie$上的$j$号节点时的最小修改数量,然后就可以枚举母串各位 ...

  5. Miller-Robin与二次探测

    素数在数论中经常被用到.也是数论的基础之一. 人们一直在讨论的问题是,怎样快速找到素数?或者判断一个数是素数? 1.根号n枚举 原始暴力方法. 2.埃氏筛 每个合数会被筛质因子次数次.复杂度O(Nlo ...

  6. CentOS 6.6下配置本地yum源与网络yum源

    一.本地yum源 1.系统默认已经安装了可使用yum的软件包,所以可以直接配置: [root@localhost ~]# cd /etc/yum.repos.d/                    ...

  7. Ansible9:条件语句

    目录 一.when 1.基本用法 2.在when中使用jinja2的语法 3.使用bool值作为when的判断条件 4.在when中使用defined关键字 5.when在循环语句中的使用方法 6.在 ...

  8. C++函数不能为virtual的场景

    1.类函数不能同时被static和virtual修饰. 2.类的模板函数不能被virtual修饰 未完待续

  9. js事件的捕获和冒泡阶段

    讨论的主要是两个事件模型:IE事件模型与DOM事件模型 IE内核浏览器的事件模型是冒泡型事件(没有捕获事件过程),事件句柄的触发顺序是从ChildNode到ParentNode. <div id ...

  10. 判断html是否含有图片

    核心代码: $url="http://XXXXX/article/012.html"; $content=file_get_contents($url); //读取文章页面源代码 ...