原文链接:应用服务器中对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. 1: @override

    @Override是Java5的元数据,自动加上去的一个标志,告诉你说下面这个方法是从父类/接口 继承过来的,需要你重写一次,这样就可以方便你阅读,也不怕会忘记 @Override是伪代码,表示重写( ...

  2. 什么是ground truth(GT)

    转自ground truth的含义 ground truth在不同的地方有不同的含义,下面是参考维基百科的解释,ground truth in wikipedia. 1.在统计学和机器学习中 在机器学 ...

  3. 贪心问题:区间覆盖 POJ 1328 Rader Installation

    题目:http://poj.org/problem?id=1328 题意:给定海岛个数,雷达半径,输入各个海岛坐标,求能覆盖所有海岛的最少雷达数 题解: 1. 贪心的区间覆盖问题,尽量让每个雷达覆盖更 ...

  4. 算法: 排序: 归并排序(Merge)

    http://www.codeproject.com/Articles/805587/Merge-Sort

  5. Python核心编程——Chapter9

    好久没写过Python了,前一阵子忙这忙那的,都几乎把Python给丢掉了,话不多说,马上开始. 9.1.文件过滤.显示一个文件的所有行,并且忽略以井号开头的行. 其实这个题目比较基础,用shell语 ...

  6. 在windows的IDEA运行Presto

    After building Presto for the first time, you can load the project into your IDE and run the server. ...

  7. 05.UIDynamic

    CHENYILONG Blog 05.UIDynamic Fullscreen © chenyilong. Powered by Postach.io Blog

  8. tensorflow中的卷积和池化层(一)

    在官方tutorial的帮助下,我们已经使用了最简单的CNN用于Mnist的问题,而其实在这个过程中,主要的问题在于如何设置CNN网络,这和Caffe等框架的原理是一样的,但是tf的设置似乎更加简洁. ...

  9. JavaScript辅助响应式

    js响应式 rem辅助响应式布局:其实就是指在HTML页面的大小不断变化的时候,里面的宽.高.字体等等也随之变化,主要是通过获取window.innerwidth的值来进行判断,7.5rem===10 ...

  10. PHP简单爬虫 爬取免费代理ip 一万条

    目标站:http://www.xicidaili.com/ 代码: <?php require 'lib/phpQuery.php'; require 'lib/QueryList.php'; ...