关于Java正则引起的StackOverFlowError有关问题以及解决方案

    现象说明:使用大量的正则表达式对网页内容进行提取并以流的方式长期运行时,抛出了StackOverflowError错误,查看日志是由java.util.regex产生的。从异常信息来看,很明显,线程栈空间不足,一般来说,存在这种情况,是因为方法的嵌套调用层次太深,上层的方法栈一直得不到释放,导致栈空间不足

    原因分析:与C和Pasca一样,JVM依然是采用栈式的虚拟机。对于每一个线程,都有一个java栈 ,函数的调用过程都体现在堆栈和退栈上了。当有一个方法被调用的时候,会产生一些跟这个方法相关的信息,如方法名、参数、中间变量等等。这些叫做栈帧,当一个方法执行完毕,这个栈帧才会从栈顶pop掉。如果方法是递归的话,会一直向栈里push栈帧,而这个java栈是有一定的长度或深度的,当栈满了,无法再进行push的时候。就出现StackOverflowError的异常了。解决办法的话,就不要用递归操作改用for,而且平时也不建议用递归的,效率太低了。

    Java在使用正则表达式的时候,底层是通过递归方式执行的,每一层的递归都会在栈线程的大小中占一定内存,如果递归的层次很多,就会报出stackOverFlowError异常。这个异常从Java 1.4就存在于java.util.regex包中,这个bug仍然存在,是已知的bug #5050507的表现,因为它是“won't fix”的状态。

    解决方案: 在对正则不是很熟悉的情况下,可以通过增加JVM的-Xss参数来解决。但修改-Xss只是治标不治本的解决方法,要从根源上解决还需要优化正则。

    关于:优化Java中的正则表达式 

    1、如果在程序中多次使用同一个正则表达式,一定要用Pattern.compile()编译,代替直接使用Pattern.matches()。如果一次次对同一个正则表达式使用Pattern.matches(),例如在循环中,没有编译的正则表达式消耗比较大。因为matches()方法每次都会预编译使用的表达式。另外,记住你可以通过调用reset()方法对不同的输入字符串重复使用Matcher对象。

    2、留意选择(Beware of alternation)。类似“(X|Y|Z)”的正则表达式有降低速度的坏名声,所以要多留心。首先,考虑选择的顺序,那么要将比较常用的选择项放在前面,因此它们可以较快被匹配。另外,尝试提取共用模式;例如将“(abcd|abef)”替换为“ab(cd|ef)”。后者匹配速度较快,因为NFA会尝试匹配ab,如果没有找到就不再尝试任何选择项。(在当前情况下,只有两个选择项。如果有很多选择项,速度将会有显著的提升。)选择的确会降低程序的速度。在我的测试中,表达式“.*(abcd|efgh|ijkl).*”要比调用String.indexOf()三次——每次针对表达式中的一个选项——慢三倍。

    3、获取每次使用引起小损失的分组。如果你实际并不需要获取一个分组内的文本,那么就使用非捕获分组。例如使用“( :X)”代替“(X)”。

你可能感兴趣的:(关于Java正则引起的StackOverFlowError有关问题以及解决方案)