歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java正則達式引起死循環問題解決辦法

Java正則達式引起死循環問題解決辦法

日期:2017/3/1 11:08:28   编辑:Linux編程

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

  1. at java.util.regex.Pattern$GroupTail.match(Unknown Source)
  2. at java.util.regex.Pattern$Ctype.match(Unknown Source)
  3. at java.util.regex.Pattern$Branch.match(Unknown Source)
  4. at java.util.regex.Pattern$GroupHead.match(Unknown Source)
  5. at java.util.regex.Pattern$Loop.match(Unknown Source)
  6. at java.util.regex.Pattern$GroupTail.match(Unknown Source)
  7. at java.util.regex.Pattern$Ctype.match(Unknown Source)
  8. at java.util.regex.Pattern$Branch.match(Unknown Source)
  9. at java.util.regex.Pattern$GroupHead.match(Unknown Source)
  10. at java.util.regex.Pattern$Loop.match(Unknown Source)
  11. at java.util.regex.Pattern$GroupTail.match(Unknown Source)
  12. at java.util.regex.Pattern$Ctype.match(Unknown Source)
  13. at java.util.regex.Pattern$Branch.match(Unknown Source)
  14. at java.util.regex.Pattern$GroupHead.match(Unknown Source)
  15. at java.util.regex.Pattern$Loop.match(Unknown Source)
  16. at java.util.regex.Pattern$GroupTail.match(Unknown Source)
  17. at java.util.regex.Pattern$Ctype.match(Unknown Source)
  18. at java.util.regex.Pattern$Branch.match(Unknown Source)
  19. at java.util.regex.Pattern$GroupHead.match(Unknown Source)
  20. at java.util.regex.Pattern$Loop.match(Unknown Source)
  21. at java.util.regex.Pattern$GroupTail.match(Unknown Source)
  22. at java.util.regex.Pattern$Ctype.match(Unknown Source)
  23. at java.util.regex.Pattern$Branch.match(Unknown Source)

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

  1. public static boolean checkSpecialChars(String inputstr, String regex)
  2. {
  3. if (inputstr == null || "".equals(inputstr))
  4. {
  5. return false;
  6. }
  7. return Pattern.compile(regex).matcher(inputstr).matches();
  8. }

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

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

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

  1. import java.util.regex.Pattern;
  2. /**
  3. * Created on 2010-11-9
  4. * <p>Title: 測試正則表達式死循環</p>
  5. * @author [email protected]
  6. * @version 1.0
  7. */
  8. public class RegexTest
  9. {
  10. public static void main(String args[])
  11. {
  12. String regex = "([a-z]|//d)*";
  13. String inputStr = "";
  14. for (int i = 0; i < 309; i++) //此處的值為>=400則會馬上拋異常
  15. {
  16. inputStr = inputStr.concat(String.valueOf(i)); //循環的拼接輸入字符串
  17. }
  18. System.out.println("字符串長度為:"+inputStr.length());
  19. boolean flag = checkSpecialChars(inputStr, regex);
  20. System.out.println("匹配結果為: "+flag);
  21. }
  22. public static boolean checkSpecialChars(String inputstr, String regex)
  23. {
  24. if (inputstr == null || "".equals(inputstr))
  25. {
  26. return false;
  27. }
  28. return Pattern.compile(regex).matcher(inputstr).matches(); //注意是此處matches()方法拋的異常
  29. }
  30. }

原來:該問題是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.linuxidc.com/Linux/2011-11/46616.htm 這篇文章,非常不錯。

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

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

Copyright © Linux教程網 All Rights Reserved