java nio的一個嚴重BUG(轉)


 

這個BUG會在linux上導致cpu 100%,使得nio server/client不可用,具體的詳情可以看這里http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933 。
令人失望的是這個BUG直到jdk 6u4才解決,sun的拖沓讓人難以相信。這個BUG在server端容易出現,因為server端有頻繁地接入斷開連接。
    
使用jdk 6u4之前版本的nio框架都有這個隱患,除非你的框架很好地處理了這個可能的隱患。Grizzly的處理方式比較簡單,也就是BUG報告里面提到的方式,在SelectionKey.cancel()之后馬上進行了一次select調用將fd從poll(epoll)中移除:

this.selectionKey.cancel();
try {
            // cancel key,then select now to remove file descriptor
            this.selector.selectNow();
 } catch (IOException e) {
         onException(e);
        log.error("Selector selectNow fail", e);
}


    實際上這樣的解決方式還是留有隱患的,因為key的取消和這個selectNow操作很可能跟Selector.select操作並發地在進行,在兩個操作之間仍然留有一個極小的時間窗口可能發生這個BUG。因此,你需要更安全地方式處理這個問題,jetty的處理方式是這樣,連續的select(timeout)操作沒有阻塞並返回0,並且次數超過了一個指定閥值,那么就遍歷整個key set,將key仍然有效並且interestOps等於0的所有key主動取消掉;如果在這次修正后,仍然繼續出現select(timeout)不阻塞並且返回0的情況,那么就重新創建一個新的Selector,並將Old Selector的有效channel和對應的key轉移到新的Selector上,

                    long before=now;
                    int selected=selector.select(wait);
                    now = System.currentTimeMillis();
                    _idleTimeout.setNow(now);
                    _timeout.setNow(now);

                    // Look for JVM bugs
                    //  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
                    if (__JVMBUG_THRESHHOLD>0 && selected==0 && wait>__JVMBUG_THRESHHOLD && (now-before)<(wait/2) )
                    {
                        _jvmBug++;
                        if (_jvmBug>=(__JVMBUG_THRESHHOLD2))
                        {
                            synchronized (this)
                            {
                                _lastJVMBug=now;
                                                    // BLOODY SUN BUG !!!  Try refreshing the entire selector.
                                final Selector new_selector = Selector.open();
                                for (SelectionKey k: selector.keys())
                                {
                                    if (!k.isValid() || k.interestOps()==0)
                                        continue;
                                    
                                    final SelectableChannel channel = k.channel();
                                    final Object attachment = k.attachment();
                                    
                                    if (attachment==null)
                                        addChange(channel);
                                    else
                                        addChange(channel,attachment);
                                }
                                _selector.close();
                                _selector=new_selector;
                                _jvmBug=0;
                                return;
                            }
                        }
                        else if (_jvmBug==__JVMBUG_THRESHHOLD || _jvmBug==__JVMBUG_THRESHHOLD1)
                        {
                            // Cancel keys with 0 interested ops
                            for (SelectionKey k: selector.keys())
                            {
                                if (k.isValid()&&k.interestOps()==0)
                                {
                                    k.cancel();
                                }
                            }
                            return;
                        }
                    }
                    else
                        _jvmBug=0;



    這個方案能比較好的在jdk 6u4之前的版本上解決這個BUG可能導致的問題。Mina和Netty沒有看到有處理這個BUG的代碼,如果我看錯了,請留言告訴我。Yanf4j一直采用的是grizzly的方式,准備加上jetty的處理方案。當然,最簡單的方案就是升級你的JDK 

http://blog.csdn.net/xyls12345/article/details/26571699


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM