diff --git a/core/src/main/java/com/taobao/arthas/core/util/RegexCacheManager.java b/core/src/main/java/com/taobao/arthas/core/util/RegexCacheManager.java new file mode 100644 index 00000000000..bf05a8c6c2e --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/util/RegexCacheManager.java @@ -0,0 +1,65 @@ +package com.taobao.arthas.core.util; + +import com.taobao.arthas.core.shell.term.impl.http.session.LRUCache; +import java.util.regex.Pattern; + +/** + * 正则表达式缓存管理器 + * 用于缓存编译后的正则表达式对象,避免重复编译的开销 + */ +public class RegexCacheManager { + private static final RegexCacheManager INSTANCE = new RegexCacheManager(); + + // 使用LRUCache缓存编译后的正则表达式 + private final LRUCache regexCache; + + // 缓存大小限制 + private static final int MAX_CACHE_SIZE = 100; + + private RegexCacheManager() { + // 初始化LRUCache,设置最大缓存大小 + this.regexCache = new LRUCache<>(MAX_CACHE_SIZE); + } + + public static RegexCacheManager getInstance() { + return INSTANCE; + } + + /** + * 获取正则表达式Pattern对象,优先从缓存获取,缓存未命中则编译并缓存 + */ + public Pattern getPattern(String regex) { + if (regex == null || regex.isEmpty()) { + return null; + } + + // 从LRUCache获取 + Pattern pattern = regexCache.get(regex); + if (pattern != null) { + return pattern; + } + + // 缓存未命中,编译正则表达式 + // 不捕获PatternSyntaxException,让异常向上抛出,以便及时发现无效的正则表达式 + pattern = Pattern.compile(regex); + // 缓存编译结果 + regexCache.put(regex, pattern); + + return pattern; + } + + /** + * 清理缓存 + */ + public void clearCache() { + regexCache.clear(); + } + + /** + * 获取缓存大小 + */ + public int getCacheSize() { + return regexCache.usedEntries(); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/util/matcher/RegexMatcher.java b/core/src/main/java/com/taobao/arthas/core/util/matcher/RegexMatcher.java index 1998265e365..e423205075c 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/matcher/RegexMatcher.java +++ b/core/src/main/java/com/taobao/arthas/core/util/matcher/RegexMatcher.java @@ -1,5 +1,8 @@ package com.taobao.arthas.core.util.matcher; +import com.taobao.arthas.core.util.RegexCacheManager; +import java.util.regex.Pattern; + /** * regex matcher * @author ralf0131 2017-01-06 13:16. @@ -7,6 +10,7 @@ public class RegexMatcher implements Matcher { private final String pattern; + private volatile Pattern compiledPattern; public RegexMatcher(String pattern) { this.pattern = pattern; @@ -14,8 +18,15 @@ public RegexMatcher(String pattern) { @Override public boolean matching(String target) { - return null != target - && null != pattern - && target.matches(pattern); + if (null == target || null == pattern) { + return false; + } + + // 在第一次matching时才编译正则表达式 + if (compiledPattern == null) { + compiledPattern = RegexCacheManager.getInstance().getPattern(pattern); + } + + return compiledPattern != null && compiledPattern.matcher(target).matches(); } } \ No newline at end of file diff --git a/core/src/test/java/com/taobao/arthas/core/util/RegexCacheManagerTest.java b/core/src/test/java/com/taobao/arthas/core/util/RegexCacheManagerTest.java new file mode 100644 index 00000000000..240a97585cc --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/util/RegexCacheManagerTest.java @@ -0,0 +1,135 @@ +package com.taobao.arthas.core.util; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * RegexCacheManager测试类 + */ +public class RegexCacheManagerTest { + private RegexCacheManager cacheManager; + + @Before + public void setUp() { + // 获取单例实例 + cacheManager = RegexCacheManager.getInstance(); + // 清理缓存,确保测试环境干净 + cacheManager.clearCache(); + } + + /** + * 测试基本缓存功能 + */ + @Test + public void testBasicCacheFunctionality() { + // 测试缓存未命中的情况 + String regex1 = ".*Test.*"; + Pattern pattern1 = cacheManager.getPattern(regex1); + Assert.assertNotNull(pattern1); + Assert.assertEquals(1, cacheManager.getCacheSize()); + + // 测试缓存命中的情况 + Pattern pattern1Cached = cacheManager.getPattern(regex1); + Assert.assertNotNull(pattern1Cached); + Assert.assertSame(pattern1, pattern1Cached); // 应该是同一个对象 + Assert.assertEquals(1, cacheManager.getCacheSize()); // 缓存大小应该保持不变 + + // 测试多个正则表达式 + String regex2 = "^Test.*"; + Pattern pattern2 = cacheManager.getPattern(regex2); + Assert.assertNotNull(pattern2); + Assert.assertEquals(2, cacheManager.getCacheSize()); + + // 测试空正则表达式 + Pattern nullPattern = cacheManager.getPattern(null); + Assert.assertNull(nullPattern); + + Pattern emptyPattern = cacheManager.getPattern(""); + Assert.assertNull(emptyPattern); + } + + /** + * 测试LRU淘汰策略 + */ + @Test + public void testLRUEvictionPolicy() { + // 生成多个正则表达式,超过最大缓存大小 + int maxCacheSize = 100; + for (int i = 0; i < maxCacheSize + 5; i++) { + String regex = "TestRegex" + i; + Pattern pattern = cacheManager.getPattern(regex); + Assert.assertNotNull(pattern); + } + + // 缓存大小应该等于最大缓存大小 + Assert.assertEquals(maxCacheSize, cacheManager.getCacheSize()); // 100 是实际的最大缓存大小 + + // 测试访问顺序,确保LRU策略生效 + String firstRegex = "TestRegex0"; + + // 再次访问第一个正则表达式,使其成为最近使用的 + Pattern firstPattern = cacheManager.getPattern(firstRegex); + Assert.assertNotNull(firstPattern); + + // 再添加一个新的正则表达式,应该淘汰最久未使用的 + String newRegex = "NewTestRegex"; + Pattern newPattern = cacheManager.getPattern(newRegex); + Assert.assertNotNull(newPattern); + + // 第一个正则表达式应该仍然在缓存中(因为刚被访问过) + Pattern firstPatternAgain = cacheManager.getPattern(firstRegex); + Assert.assertNotNull(firstPatternAgain); + } + + /** + * 测试缓存清理功能 + */ + @Test + public void testCacheClear() { + // 添加一些缓存项 + cacheManager.getPattern(".*Test1"); + cacheManager.getPattern(".*Test2"); + Assert.assertTrue(cacheManager.getCacheSize() > 0); + + // 清理缓存 + cacheManager.clearCache(); + Assert.assertEquals(0, cacheManager.getCacheSize()); + + // 清理后应该可以重新添加缓存项 + Pattern pattern = cacheManager.getPattern(".*Test3"); + Assert.assertNotNull(pattern); + Assert.assertEquals(1, cacheManager.getCacheSize()); + } + + /** + * 测试无效正则表达式处理 + */ + @Test + public void testInvalidRegexHandling() { + // 测试无效的正则表达式,应该抛出PatternSyntaxException + String invalidRegex = "[a-z"; + try { + cacheManager.getPattern(invalidRegex); + } catch (Exception e) { + // 验证抛出的是PatternSyntaxException + Assert.assertTrue("Expected PatternSyntaxException but got " + e.getClass().getName(), e instanceof PatternSyntaxException); + } + + // 测试另一个无效的正则表达式,应该抛出PatternSyntaxException + String anotherInvalidRegex = "(a-z"; + try { + cacheManager.getPattern(anotherInvalidRegex); + } catch (Exception e) { + // 验证抛出的是PatternSyntaxException + Assert.assertTrue("Expected PatternSyntaxException but got " + e.getClass().getName(), e instanceof PatternSyntaxException); + } + + // 确保缓存大小没有增加 + Assert.assertEquals("无效正则表达式不应该被缓存", 0, cacheManager.getCacheSize()); + } + +}