Java HeapDump分析:String还是StringBuilder,应该用什么?

Java HeapDump Analysis: String or StringBuilder, what should be used?

在我的应用程序中有一个堆转储,令人惊讶的是 char[] 保留的堆大约为 700MB,这很奇怪(至少对我而言)。同时String只有150MB。

在我的应用程序中,我只使用了 StringBuilder(使用默认的 StringBuilder 构造函数)并在我们追加数据时尽量避免使用 String

我的问题是:我们应该一直选择 StringBuilder 吗?如果是,我们如何减少它保留的堆?

可能您可以使用 StringInterner,它采用 StringBuilder 来避免创建对象 needlessly.You 首先用文本填充回收的 StringBuilder,如果与该文本匹配的字符串在内部,则返回该字符串(或StringBuilder 的 toString() 是。)好处是您只在看到新的字符串(或至少一个不在数组中的字符串)时创建对象(并且不会超过需要的对象)这可以获得 80% 到 99% 的命中率在加载许多数据字符串时显着降低内存消耗(和垃圾)。

代码: https://github.com/OpenHFT/Java-Lang/blob/master/lang/src/main/java/net/openhft/lang/pool/StringInterner.java

是的,在构建字符串时总是选择 StringBuilder - 这是连接字符串的最有效但仍然方便的方法。

听起来好像有很多 StringBuilder 等待被垃圾收集。但是,为了减少堆使用,您可以安全地 重用 您的 StringBuilder,即使它们不是线程安全的,通过 Threadlocal 每个线程使用一个 StringBuilder :

private static final ThreadLocal<StringBuilder> LOCAL_STRING_BUILDER =
    ThreadLocal.withInitial(StringBuilder::new);

用法示例:

public String logMessage() {
    StringBuilder sb = LOCAL_STRING_BUILDER.get();
    sb.setLength(0); // Only resets the pointer to start. Doesn't affect the backing array
    sb.append("foo=").append(myField); //etc
    return sb.toString();
}

您最多只能拥有与线程一样多的 StringBuilder,不会 那么 很多(可能是 10 到 100 条)。


仅供参考 StringBuilder 用于手动连接字符串;这行源码:

String str3 = str1 + str2;

被编译为:

String str3 = new StringBuilder().append(str1).append(str2).toString();