专栏首页TopCoderJava nio 空轮询bug到底是什么

Java nio 空轮询bug到底是什么

编者注:Java nio 空轮询bug也就是Java nio在Linux系统下的epoll空轮询问题。

epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说。其高效的原因是将基于事件的fd放到内核中来完成,在内核中基于红黑树+链表数据结构来实现,链表存放有事件发生的fd集合,然后在调用epoll_wait时返回给应用程序,由应用程序来处理这些fd事件。

使用IO复用,Linux下一般默认就是epoll,Java NIO在Linux下默认也是epoll机制,但是JDK中epoll的实现却是有漏洞的,其中最有名的java nio epoll bug就是即使是关注的select轮询事件返回数量为0,NIO照样不断的从select本应该阻塞的Selector.select()/Selector.select(timeout)中wake up出来,导致CPU 100%问题。如下图所示:

那么产生这个问题的原因是什么的?其实在 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6670302 上已经说明的很清楚了,比如下面是bug复现的一个场景:

 1A DESCRIPTION OF THE PROBLEM :
 2The NIO selector wakes up infinitely in this situation..
 30. server waits for connection
 41. client connects and write message
 52. server accepts and register OP_READ
 63. server reads message and remove OP_READ from interest op set
 74. client close the connection
 85. server write message (without any reading.. surely OP_READ is not set)
 96. server's select wakes up infinitely with return value 0
10

上面的场景描述的问题就是连接出现了RST,因为poll和epoll对于突然中断的连接socket会对返回的eventSet事件集合置为POLLHUP或者POLLERR,eventSet事件集合发生了变化,这就导致Selector会被唤醒,进而导致CPU 100%问题。根本原因就是JDK没有处理好这种情况,比如SelectionKey中就没定义有异常事件的类型。

1class SelectionKey {
2    public static final int OP_READ = 1 << 0;
3    public static final int OP_WRITE = 1 << 2;
4    public static final int OP_CONNECT = 1 << 3;
5    public static final int OP_ACCEPT = 1 << 4;
6}

既然nio epoll bug存在,那么能不能规避呢?答案是有的,比如netty就很巧妙的规避了这个问题,它的处理机制就是如果发生了这种情况,并且发生次数超过了SELECTOR_AUTO_REBUILD_THRESHOLD(默认512),则调用rebuildSelector()进行Selecttor重建,这样就不用管之前发生了异常情况的那个连接了。因为重建也是根据SelectionKey事件对应的连接来重新注册的。

该问题最早在 Java 6 发现,随后很多版本声称解决了该问题,但实际上只是降低了该 bug 的出现频率,目前从网上搜索到的资料显示,Java 8 还是存在该问题(当 Thrift 遇到 JDK Epoll Bug)。

最后一起来分析下,nio epoll bug不是linux epoll的问题,而是JDK自己实现epoll时没有考虑这种情况,或者说因为其他系统不存在这个问题,Java为了封装(比如SelectionKey 中的4个事件类型)的统一而没去处理?

这里思考下,如果想要从java nio层面上来解决这个问题,该如何做呢?

一种是nio事件类型SelectionKey新加一种"错误"类型,比如针对linux epoll中的epollhup和epollerr,如果出现这种事件,建议程序直接close socket,但这种方式相对来说对于目前的nio SelectionKey改动有点大,因为SelectionKey的定义目前是针对所有jdk平台的;还有一种是针对jdk nio 对epoll的封装中,对于epoll的epollhup和epollerr事件,epoll封装内部直接处理,比如close socket,但是这种方案也有一点尴尬的是,可能上层应用代码还保留有出现问题的socket引用,这时最好是应用程序能够感知这种情况来处理比较好。

Java nio空转问题由来已久,一般程序中是通过新建Selector的方式来屏蔽掉了JDK5/6的这个问题,因此,对于开发者来讲,还是尽量将JDK的版本更新到最新,或者使用NIO框架如Netty,Grizzly等进行研发,以免出更多的问题。

推荐阅读

本文分享自微信公众号 - TopCoder(gh_12e4a74a5c9c)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 5 门正在奋力崛起的编程语言

    在软件项目与具体实现层面,我们需要考虑众多具体因素。但无论从哪种角度出发,技术栈的选择永远是决定项目成功与否的核心因素之一。根据您的实际应用需求、站点或者产品设...

    GitHubDaily
  • [译]JS 模块化历史简介

    对于 JavaScript 来说,模块化是一个相对现代的概念,这篇文章会带你在 JavaScript 的世界里快速浏览模块化的历史进程~

    savokiss
  • OpenRASP梳理总结

    RASP英文为 Runtime application self-protection,即运行时应用程序自我保护。“运行时应用程序自我保护”的概念由Gartne...

    FB客服
  • 从Reactor到WebFlux

    为了应对高并发场景下到服务端编程需求,微软最先提出了一种异步编程到方案Reactive Programming,也就是反应式编程。

    春哥大魔王
  • Android | 如何使程序实现线程安全(拓展关键词:ThreadLocal、重排序、volatile/final)

    假设Thread1 为 writer线程,初始化了一个FinalFieldExample实例f, Thread2 为 reader线程,读取实例f 的x、y值...

    凌川江雪
  • 类图画法?这样记

    很多新手甚至老手有时候都无法徒手画类图,其中原因可能就是关系线太难记。下面是我总结的类图UML中的概念和Java中的映射,以及对应的关系线。

    ImportSource
  • Spring周边:Formatter(字符串格式化)

    Formatter 通过将程序使用的数据的二进制形式转换成格式化的文本进行工作,这个格式化动作在“缓冲区”中进行,可以让 Formatter 自动提供这个缓冲区...

    WEBJ2EE
  • Spring周边:国际化、Format

    Locale 类代表一个特定的地理、语言和国家环境。Locale 向一些对国家和语言、地理等比较敏感的类提供国家地区语言信息,这些类有DateFormat、Nu...

    WEBJ2EE
  • 一份不可多得的 Lombok 学习指南

    Lombok 是一款 Java 开发插件,使得 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(P...

    江南一点雨
  • Apache Solr最新RCE漏洞分析

    Apache Solr爆出RCE 0day漏洞(漏洞编号未给出),这里简单的复现了对象,对整个RCE的流程做了一下分析,供各位看官参考。

    FB客服

扫码关注云+社区

领取腾讯云代金券