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这个类下面。

转载于:https://www.cnblogs.com/JAYIT/p/8241634.html

NIO的epoll空轮询bug相关推荐

  1. NIO的空轮询bug是什么?netty是如何解决NIO空轮询bug的?

    文章目录 1. NIO的空轮询bug 2. netty如何解决NIO空轮询bug的? 1. NIO的空轮询bug JDK1.5开始引入了epoll基于事件响应机制来优化NIO.相较于select和po ...

  2. Netty : 臭名昭著的JDK的NIO bug(空轮询bug)

    1.美图 2.概述 在搞这个问题的时候 Netty:Netty不断打开文件的BUG 一直找不到原因,然后偶然想了一下是不是netty的空轮询bug,后来查了一下真的是,第一感觉居然是,我草,我草,好兴 ...

  3. Netty : netty 4如何解决空轮询bug

    1.美图 2.概述 空轮询bug参考:Netty : 臭名昭出的JDK的NIO bug(空轮询bug) 4.netty4 解决 4.1 构建阈值 int selectorAutoRebuildThre ...

  4. Netty : netty 3如何解决空轮询bug

    1.美图 2.概述 空轮询bug参考:Netty : 臭名昭出的JDK的NIO bug(空轮询bug) 3. netty 3 如何解决 netty3采用的是第三种方案,检测重点是select函数是否返 ...

  5. java nio空轮循_Java nio 空轮询bug到底是什么

    epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效的原因是将基于事件的fd放到内核中来完成,在内核中基于红黑树+链表数据结构来实现,链表存放有事件发生的f ...

  6. java为什么不解决空轮询,netty解决空轮询bug

    selector在没有结果的情况下,依然被唤醒,导致一直空轮询,cpu100% 直接定位到NioEventLoop @Override protected void run() { for (;;) ...

  7. Selector空轮询

    JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%.官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该 ...

  8. 面试必会系列 - 5.1 网络BIO、NIO、epoll,同步/异步模型、阻塞/非阻塞模型,你能分清吗?

    本文已收录至 Github(MD-Notes),若博客中图片模糊或打不开,可以来我的 Github 仓库,包含了完整图文:https://github.com/HanquanHq/MD-Notes,涵 ...

  9. 【操作系统三】图解网络IO(bio\nio\slect\epoll)

    [操作系统三]图解网络IO+实战 一.计算机组成 二.系统中断 三.晶振(时间中断.分时复用) 四.事件中断 1.DMA 2.事件中断 3.网卡也会产生中断? 五.linux系统知识 1.linux下 ...

  10. NIO 与 epoll

    文章目录 epoll 与 select 第一部分:select 和 epoll的任务 select 的做法 epoll的做法 第二部分:epoll详解 epoll 系统调用 epoll高效的原因 ep ...

最新文章

  1. Go 语言 2019 调查报告发布(内含 Go 语言图谱下载)
  2. oracle大批量数据统计,加速Oracle大批量数据处理的2个好用方案
  3. Python编程基础12:函数和模块
  4. java如何解析word大纲_Java POI 解析word文档
  5. linux crontab 每隔一段时间执行一次
  6. 小米手机抓取Log教程
  7. JavaScript留言板效果代码实例
  8. 用supermemo背单词达到5000词条
  9. 怎么快速查找重复文件以及删除重复文件
  10. 视频教程-Linux C语言编程基础视频精讲-C/C++
  11. 什么是存储过程?用什么调用
  12. 良心到难以置信的网站推荐第7期丨全程干货
  13. 【Mockplus教程】MAC上安装Mockplus
  14. iTunes12.9以上 自定义铃声 无法拖入 无法添加
  15. .h和.c文件的区别到底是什么(精确讲解)
  16. 国内PLM用户满意度调研完整版
  17. 这些大厂笔试题 你都见识(被无情鞭挞)过了吗?—— 哔哩哔哩篇
  18. UI界面布局有哪些?
  19. java word 模板_java通过word模板生成word文档
  20. 计算机的风扇参数,风扇能力大不同 这个参数来区分

热门文章

  1. 关于Adobe AIR 获取屏幕信息及任务栏高度.
  2. H3C BGP基本配置
  3. 网络核心之数据交换 - 报文、分组交换
  4. Docker详解(三)——Docker安装与部署
  5. NYOJ--40--公约数和公倍数
  6. 教你大数据必修三大技能 快快记录下来
  7. Shell命令-网络操作之基础之telnet、ssh
  8. uva 104 Arbitrage (DP + floyd)
  9. php 文件上传$_FILES中error返回值详解
  10. laravel后台可修改配置自动加载