前几天遇到了一个由正则表达式引起的线上事故,来跟大家分享下,希望能够帮助到大家,具体的排查过程请见
Java中的JVM指令和Arthas以及Dump文件(jvisualvm和MemoryAnalyzer工具)整体分析
先看以下代码
Pattern pattern = Pattern.compile(input, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(source);
当我们业务中有需要使用正则表达式的时候,可能会用到Pattern
和Matcher
两个类,它们是JDK编译过程中非常重要的两个类,在使用过程中需要注意以下几点:
compile
方法时里面使用大量的对象来记录相关的状态,其中包括字节数组buffer的填充,以及一些数组的拷贝,以及相关的状态变量等等,口说无凭,我们来大致看一下compile
即可请跟着我看下源码
public static Pattern compile(String regex, int flags) {
return new Pattern(regex, flags);
}
private Pattern(String p, int f) {
pattern = p;
flags = f;
// to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
if ((flags & UNICODE_CHARACTER_CLASS) != 0)
flags |= UNICODE_CASE;
// Reset group index count
capturingGroupCount = 1;
localCount = 0;
if (!pattern.isEmpty()) {
try {
// 重点关注下
compile();
} catch (StackOverflowError soe) {
throw error("Stack overflow during pattern compilation");
}
} else {
root = new Start(lastAccept);
matchRoot = lastAccept;
}
}
private void append(int ch, int len) {
if (len >= buffer.length) {
int[] tmp = new int[len+len];
System.arraycopy(buffer, 0, tmp, 0, len);
buffer = tmp;
}
buffer[len] = ch;
}
public Matcher matcher(CharSequence input) {
if (!compiled) {
// 这里还是用了同步锁机制
synchronized(this) {
if (!compiled)
compile();
}
}
Matcher m = new Matcher(this, input);
return m;
}
关于Pattern这个类,可以看到正则表达式编译的大概过程,如果你的正则表达式比较复杂,建议做下拆分
这个类负责将Pattern编译后的正则表达式与目标源文件进行匹配,它是逐个字符去匹配的过程,而且还是使用了synchronized
同步锁的关键字,意味着当业务中许多地方存在匹配逻辑,是只能有一个线程进行匹配的
Pattern pattern = null;
Matcher matcher = null;
try {
pattern = Pattern.compile(input, Pattern.MULTILINE);
matcher = pattern.matcher(source);
while (matcher.find()) {
// start 5542 5563
ContractFunctionBody item = new ContractFunctionBody(matcher.group(), matcher.start(), matcher.end());
matchedItems.add(item);
}
} finally {
// 显式释放资源
matcher = null;
pattern = null;
}