正则表达式引起的性能下降

背景:在学习性能优化的知识时,发现一个因为正则表达式引起的性能下降问题,记录下来

藏在正则表达式里的陷阱

转载 藏在正则表达式里的陷阱

通过java自带的线程dump工具 发现出问题的线程 是字符串的校验

ps:也可以通过代码注释的方式,发现出问题的地方就是正则校验

其实这里导致 CPU 使用率高的关键原因就是:Java 正则表达式使用的引擎实现是 NFA 自动机,这种正则表达式引擎在进行字符匹配时会发生回溯(backtracking)。而一旦发生回溯,那其消耗的时间就会变得很长,有可能是几分钟,也有可能是几个小时,时间长短取决于回溯的次数和复杂度。

正则表达式基础知识

正则表达式是一个用正则符号写出的公式,程序对这个公式进行语法分析,建立一个语法分
析树,再根据这个分析树结合正则表达式的引擎生成执行程序(这个执行程序我们把它称作
状态机,也叫状态自动机),用于字符匹配。
而这里的正则表达式引擎就是一套核心算法,用于建立状态机。
目前实现正则表达式引擎的方式有两种:DFA 自动机(Deterministic Final Automata 确
定有限状态自动机)和 NFA 自动机(Non deterministic Finite Automaton 非确定有限
状态自动机)。

说起回溯陷阱,要先从正则表达式的引擎说起。正则引擎主要可以分为基本不同的两大类:一种是DFA(确定型有穷自动机),另一种是NFA(不确定型有穷自动机)。简单来讲,NFA 对应的是正则表达式主导的匹配,而 DFA 对应的是文本主导的匹配。

DFA从匹配文本入手,从左到右,每个字符不会匹配两次,它的时间复杂度是多项式的,所以通常情况下,它的速度更快,但支持的特性很少,不支持捕获组、各种引用等等;而NFA则是从正则表达式入手,不断读入字符,尝试是否匹配当前正则,不匹配则吐出字符重新尝试,通常它的速度比较慢,最优时间复杂度为多项式的,最差情况为指数级的。但NFA支持更多的特性,因而绝大多数编程场景下(包括java,js),我们面对的是NFA。

假设一个字符串的长度是 n,如果用 DFA 自动机作为正则表达式引擎,则匹配的时间复杂
度为 O(n);如果用 NFA 自动机作为正则表达式引擎,由于 NFA 自动机在匹配过程中存在
大量的分支和回溯,假设 NFA 的状态数为 s,则该匹配算法的时间复杂度为 O(ns)。
NFA 自动机的优势是支持更多功能。例如,捕获 group、环视、占有优先量词等高级功
能。这些功能都是基于子表达式独立进行匹配,因此在编程语言里,使用的正则表达式库都
是基于 NFA 实现的
 
其实在正则表达式中有这么三种模式:贪婪模式、懒惰模式、独占模式。

在关于数量的匹配中,有 + ? * {min,max} 四种两次,如果只是单独使用,那么它们就是贪婪模式。

如果在他们之后加多一个 ? 符号,那么原先的贪婪模式就会变成懒惰模式,即尽可能少地匹配。但是懒惰模式还是会发生回溯现象的。

如果在他们之后加多一个 + 符号,那么原先的贪婪模式就会变成独占模式,即尽可能多地匹配,但是不回溯。

最后推荐一个网站,这个网站可以检查你写的正则表达式和对应的字符串匹配时会不会有问题。

Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript

 

 

 一个由正则表达式引发的血案 - 明志健致远 - 博客园

一个由正则表达式引发的血案 - 明志健致远 - 博客园

ps:这个文章对于 贪婪模式  懒惰模式 独占模式讲的很清晰,看完受益匪浅

也可以作为一个解决cpu 100%耗时问题的案例

 

posted @ 2022-04-28 09:55  CS408  阅读(1476)  评论(0编辑  收藏  举报