1.前言
前几天遇到了一个由正则表达式引起的线上事故,来跟大家分享下,希望能够帮助到大家,具体的排查过程请见
Java中的JVM指令和Arthas以及Dump文件(jvisualvm和MemoryAnalyzer工具)整体分析
先看以下代码
Pattern pattern = Pattern.compile(input, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(source);
当我们业务中有需要使用正则表达式的时候,可能会用到Pattern
和Matcher
两个类,它们是JDK编译过程中非常重要的两个类,在使用过程中需要注意以下几点:
- Pattern在调用
compile
方法时里面使用大量的对象来记录相关的状态,其中包括字节数组buffer的填充,以及一些数组的拷贝,以及相关的状态变量等等,口说无凭,我们来大致看一下compile
即可 - 如果你要编译的source即目标源文件内容不是很大,或者说你在一个正则编译完之后,处理的逻辑不复杂,能够让JVM在第一时间内回收这些对象,对业务来说是没有太大影响的。
- 如果在业务过程中使用了大量的正则表达式,在获取编译后的结果之后,要手动释放掉这些对象,否则它们将会引起内存泄漏,让服务器的CPU和内存处于一种高负载的情况
2.原因
请跟着我看下源码
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;
}
2.1 Pattern
关于Pattern这个类,可以看到正则表达式编译的大概过程,如果你的正则表达式比较复杂,建议做下拆分
2.2 Matcher
这个类负责将Pattern编译后的正则表达式与目标源文件进行匹配,它是逐个字符去匹配的过程,而且还是使用了synchronized
同步锁的关键字,意味着当业务中许多地方存在匹配逻辑,是只能有一个线程进行匹配的
3.解决示例
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;
}