Java中Pattern、Matcher使用过程中的内存泄漏风险

发布时间:2024年01月22日

1.前言

前几天遇到了一个由正则表达式引起的线上事故,来跟大家分享下,希望能够帮助到大家,具体的排查过程请见
Java中的JVM指令和Arthas以及Dump文件(jvisualvm和MemoryAnalyzer工具)整体分析

先看以下代码

        Pattern pattern = Pattern.compile(input, Pattern.MULTILINE);
        Matcher matcher = pattern.matcher(source);

当我们业务中有需要使用正则表达式的时候,可能会用到PatternMatcher两个类,它们是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;
        }
文章来源:https://blog.csdn.net/Cover_sky/article/details/135749711
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。