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;

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

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

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

以Netty为例,具体位置在NioSelector的实现类AbsNioSelector中,思路和Jetty的处理方式几乎是一样的,就是netty讲重建Selector的过程抽取成了一个方法,叫做rebuildSelector,可以看看其方法:

public void rebuildSelector() {if (!inEventLoop()) {execute(new Runnable() {@Overridepublic void run() {rebuildSelector();}});return;}final Selector oldSelector = selector;final Selector newSelector;if (oldSelector == null) {return;}try {newSelector = openSelector();} catch (Exception e) {logger.warn("Failed to create a new Selector.", e);return;}// Register all channels to the new Selector.int nChannels = 0;for (;;) {try {for (SelectionKey key: oldSelector.keys()) {Object a = key.attachment();try {if (!key.isValid() || key.channel().keyFor(newSelector) != null) {continue;}int interestOps = key.interestOps();key.cancel();SelectionKey newKey = key.channel().register(newSelector, interestOps, a);if (a instanceof AbstractNioChannel) {// Update SelectionKey((AbstractNioChannel) a).selectionKey = newKey;}nChannels ++;} catch (Exception e) {logger.warn("Failed to re-register a Channel to the new Selector.", e);if (a instanceof AbstractNioChannel) {AbstractNioChannel ch = (AbstractNioChannel) a;ch.unsafe().close(ch.unsafe().voidPromise());} else {@SuppressWarnings("unchecked")NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;invokeChannelUnregistered(task, key, e);}}}} catch (ConcurrentModificationException e) {// Probably due to concurrent modification of the key set.continue;}break;}selector = newSelector;try {// time to close the old selector as everything else is registered to the new oneoldSelector.close();} catch (Throwable t) {if (logger.isWarnEnabled()) {logger.warn("Failed to close the old Selector.", t);}}logger.info("Migrated " + nChannels + " channel(s) to the new Selector.");}

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

分析到这里,可以看到为什么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

Selector空轮询相关推荐

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

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

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

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

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

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

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

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

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

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

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

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

  7. Netty框架之Selector轮询器

    Selector的原理详解 1.传统多线程网络通信的服务器设计 2.线程池版网络通信的服务器设计 Selector版网络通信的服务器设计 说到Selector的作用,我们不得不引入 多线程网络通信的设 ...

  8. 再有人问你Netty是什么,就把这篇文章发给他

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 本文转载于公众号:Hollis 本文基于Netty4.1展开介绍相关理论模型,使用场景,基本组件. ...

  9. 消息中间件—RocketMQ的RPC通信(二

    作者:胡宗棠 来源:匠心独运的博客 在(一)篇中主要介绍了RocketMQ的协议格式,消息编解码,通信方式(同步/异步/单向).消息发送/接收以及异步回调的主要通信流程.而本篇将主要对RocketMQ ...

最新文章

  1. 【Harvest源码分析】获取F0轮廓
  2. python结果输出到文件-python print输出到文件
  3. POJ NOI MATH-7828 最大公约数与最小公倍数
  4. .NET Core 3.0 稳定版发布
  5. SQL 2005 Reporting Services:物理分页和逻辑分页 SSRS 2008 report export to PDF - Cannot get size to work...
  6. 千万级用户-亿级请求的平台架构演变
  7. 打印图形(内测第1届第1题)
  8. Atitit 信息系统安全法 目录 1. 常见的安全保护目标 1 2. WEB安全风险行为 2 2.1. Injection 2 2.2. Broker Authentication损坏的身份验证
  9. Object-c基础语法
  10. 一张图看懂财务报表分析
  11. 卸载精灵(bue directx) r4.0 完美版 绿色
  12. 深度 | 刘群:基于深度学习的自然语言处理,边界在哪里?
  13. 第二章 前端开发——JavaScript
  14. BAT面试经验分享(机器学习算法岗)
  15. 用于android的音乐可视化工具,Android音乐播放可视化
  16. 服务器usb电源型号,锂电池、龙威305D电源、故障平板、同轴电缆、惠普服务器电源、4T硬盘、USB显卡、AP等...
  17. 联想服务器双系统安装教程,联想笔记本装win8/win10双系统教程
  18. 【无标题】常见文件扩展名(后缀)及其对应文件类型和功能
  19. 快餐店促销活动流程,快餐店网络营销方案
  20. [深入研究4G/5G/6G专题-34]: URLLC-5-《中国联通5G URLLC技术白皮书3.0版本》解读-1-业务场景

热门文章

  1. Unity3d游戏引擎Windy系列教程:常见组件扫盲讲解引入unity所需的脚本语言基础
  2. 真杜比全景声家庭影院级投影设备,当贝做到了五千元内也支持
  3. sql查询电话号敏感数据加* 的写法
  4. iOS工程师 - 简历
  5. 什么样的打码网站算正规的打码网站
  6. 软件开发基于JavaScript实现快速转换文本语言(繁体中文和简体中文)_javascript技巧
  7. java实现消息队列以及延迟消息(队列DelayQueue)
  8. Java毕业设计-会议室预约小程序系统
  9. 网易考拉API,根据ID取产品详情 OneBound数据
  10. 基于java+ssm+mysql的大学生考勤管理系统及智能分析系统