Java正則達式引起死迴圈問題解決辦法

來源:互聯網
上載者:User

最近線上應用一直LOAD值非常高,幾乎接近宕機的邊緣,開始報異常如下:

at java.util.regex.Pattern$GroupTail.match(Unknown Source) at java.util.regex.Pattern$Ctype.match(Unknown Source) at java.util.regex.Pattern$Branch.match(Unknown Source) at java.util.regex.Pattern$GroupHead.match(Unknown Source) at java.util.regex.Pattern$Loop.match(Unknown Source) at java.util.regex.Pattern$GroupTail.match(Unknown Source) at java.util.regex.Pattern$Ctype.match(Unknown Source) at java.util.regex.Pattern$Branch.match(Unknown Source) at java.util.regex.Pattern$GroupHead.match(Unknown Source) at java.util.regex.Pattern$Loop.match(Unknown Source) at java.util.regex.Pattern$GroupTail.match(Unknown Source) at java.util.regex.Pattern$Ctype.match(Unknown Source) at java.util.regex.Pattern$Branch.match(Unknown Source) at java.util.regex.Pattern$GroupHead.match(Unknown Source) at java.util.regex.Pattern$Loop.match(Unknown Source) at java.util.regex.Pattern$GroupTail.match(Unknown Source) at java.util.regex.Pattern$Ctype.match(Unknown Source) at java.util.regex.Pattern$Branch.match(Unknown Source) at java.util.regex.Pattern$GroupHead.match(Unknown Source) at java.util.regex.Pattern$Loop.match(Unknown Source) at java.util.regex.Pattern$GroupTail.match(Unknown Source) at java.util.regex.Pattern$Ctype.match(Unknown Source) at java.util.regex.Pattern$Branch.match(Unknown Source) 

通過異常資訊抓取定位到我們的一個工具方法:該工具方法如下:

public static boolean checkSpecialChars(String inputstr, String regex) { if (inputstr == null || "".equals(inputstr)) { return false; } return Pattern.compile(regex).matcher(inputstr).matches(); } 

沒有任何地方是通過迴圈的調用本方法的,但根據異常資訊很明顯是死迴圈,這就引起我們進一步去跟蹤問題,通過一段時間的測試和總結,終於找到問題的產生原因。該方法允許傳一個Regex進去, 問題就出在傳入的Regex上,該運算式簡化為如下:

String regex = "([a-z]|//d)*"; 

通過測試發現,此時若輸入的字串裡面匹配次數超過817次以後,該方法將變的不穩定,開始重現我們前面的異常資訊。測試代碼如下:

import java.util.regex.Pattern; /** * Created on 2010-11-9 * <p>Title: 測試Regex死迴圈</p> * @author shixing_11@sina.com * @version 1.0 */ public class RegexTest { public static void main(String args[]) { String regex = "([a-z]|//d)*"; String inputStr = ""; for (int i = 0; i < 309; i++) //此處的值為>=400則會馬上拋異常 { inputStr = inputStr.concat(String.valueOf(i)); //迴圈的拼接輸入字串 } System.out.println("字串長度為:"+inputStr.length()); boolean flag = checkSpecialChars(inputStr, regex); System.out.println("匹配結果為: "+flag); } public static boolean checkSpecialChars(String inputstr, String regex) { if (inputstr == null || "".equals(inputstr)) { return false; } return Pattern.compile(regex).matcher(inputstr).matches(); //注意是此處matches()方法拋的異常 } }

原來:該問題是JDK的BUG,到JDK1.6裡居然還沒修複,BUG詳情見:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507和

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6988218

附:也可以參考 http://www.99inf.net/SoftwareDev/Java/53834.htm 這篇文章,非常不錯。

 

 

通過上面方法解決上拋異常問題,修改完機器重啟後發現異常是不拋了,但CPU佔用率高並沒有好轉,頻頻警示,經過仔細排查,有五個處理正則的線程把CPU資源耗完了,實在沒招,最後校正採用其它方法,徹底幹掉正則。

 

總結:通過這次線上問題排查,Regex是個雙仞劍,如果大規模資料的校正最好不要使用正則,效率非常差。CPU的處理能力會全部耗費在處理這幾個正則上。另外該問題是項目上線一段時間後才出現,這說明當資料達到一個數量級後,正則的處理效率會快速下降,這樣就像我這種情況,剛開始資料量小,一直沒有問題,等到訪問量突然增大後,CPU在短時間內LOAD值非常高。所以正則輕易不要用在大資料量或者並發訪問較高的應用中。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.