我的应用程序是多线程的,具有密集的字符串处理。我们正在经历过多的内存消耗,分析表明这是由于 String 数据造成的。我认为内存消耗会从使用某种享元模式实现甚至缓存中受益匪浅(我确信字符串经常重复,尽管我没有这方面的任何硬数据)。
我看过 Java 常量池和 String.intern,但它似乎会引发一些 PermGen 问题。
在 Java 中实现应用程序范围内的多线程字符串池的最佳替代方案是什么?
编辑:另见我之前的相关问题:How does java implement flyweight pattern for string under the hood?
最佳答案
注意:此答案使用的示例可能与现代运行时 JVM 库无关。特别是,substring
示例在 OpenJDK/Oracle 7+ 中不再是问题。
我知道这与人们经常告诉您的相反,但有时显式创建新的 String
实例可能是减少内存的重要方法。
因为字符串是不可变的,所以有几种方法利用了这一事实并共享支持字符数组以节省内存。但是,有时这实际上可以通过防止对这些数组的未使用部分进行垃圾回收来增加内存。
例如,假设您正在解析日志文件的消息 ID 以提取警告 ID。您的代码看起来像这样:
//Format:
//ID: [WARNING|ERROR|DEBUG] Message...
String testLine = "5AB729: WARNING Some really really really long message";
Matcher matcher = Pattern.compile("([A-Z0-9]*): WARNING.*").matcher(testLine);
if ( matcher.matches() ) {
String id = matcher.group(1);
//...do something with id...
}
但是看看实际存储的数据:
//...
String id = matcher.group(1);
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
char[] data = ((char[])valueField.get(id));
System.out.println("Actual data stored for string \"" + id + "\": " + Arrays.toString(data) );
这是整个测试行,因为匹配器只是将一个新的 String 实例包装在相同的字符数据周围。比较将 String id = matcher.group(1);
替换为 String id = new String(matcher.group(1));
时的结果。
关于java - Java 中 String flyweight 实现的最佳替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2915511/