一尘不染

Java中String轻量级实现的最佳替代方案

java

我的应用程序是带有密集字符串处理的多线程。我们正在经历过多的内存消耗,并且性能分析表明这是由于String数据引起的。我认为使用某种flyweight模式实现甚至是缓存将极大地受益于内存消耗(我可以肯定Strings通常是重复的,尽管我在这方面没有任何硬数据)。

我看过Java常量池和String.intern,但似乎可以引发一些PermGen问题。

在Java中实现应用程序范围的多线程字符串池的最佳替代方法是什么?


阅读 233

收藏
2020-12-03

共1个答案

一尘不染

注意:此答案使用的示例可能与现代运行时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));

2020-12-03