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

Java HeapDump分析:String还是StringBuilder,应该使用什么?,java,string,stringbuilder,heap-memory,Java,String,Stringbuilder,Heap Memory,在我的应用程序中有一个堆转储,char[]保留的堆大约有700MB,这很奇怪(至少对我来说是这样)。同时String只有150MB 在我的应用程序中,我只使用了StringBuilder(使用默认的StringBuilder构造函数),并在添加数据时尝试避免使用String 我的问题是:我们应该一直使用StringBuilder?如果是,我们如何减少它所保留的堆?您可以使用StringInterner,它使用StringBuilder来避免不必要地创建对象。您首先用文本填充回收的StringBu

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

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


我的问题是:我们应该一直使用
StringBuilder
?如果是,我们如何减少它所保留的堆?

您可以使用StringInterner,它使用StringBuilder来避免不必要地创建对象。您首先用文本填充回收的StringBuilder,如果interner中有与该文本匹配的字符串,则返回该字符串(或者使用StringBuilder的toString()这样做的好处是,当您看到一个新字符串(或者至少有一个不在数组中)时,您只需创建对象(并且不超过所需),这样可以获得80%到99%的命中率,并在加载多个数据字符串时显著减少内存消耗(和垃圾)

代码:

是的,在构建字符串时始终选择
StringBuilder
——这是连接字符串的最有效但仍然很方便的方法

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

private static final ThreadLocal<StringBuilder> LOCAL_STRING_BUILDER =
    ThreadLocal.withInitial(StringBuilder::new);
您最多只能拥有与线程数量相同的
StringBuilder
s,而不会有那么多(可能是10到100个)


FYI
StringBuilder
在手动连接字符串时使用;这一行来源:

String str3 = str1 + str2;
将其编译为:

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

相关:如果始终使用默认构造函数
StringBuilder
,则在填充时,它可能需要在内部继续分配更大的
char[]
实例(需要大约两倍的空间,就好像用户事先知道最终的大小一样)。但可能更重要的是,当您构建最终的
字符串时,必须复制
StringBuilder
中的
char[]
。完成附加后,您可能希望从
StringBuilder
中获取完成的
字符串,然后扔掉
StringBuilder
。@Andy Turner:我们将所有字符串变量声明为最终变量,因为它们都是局部变量。@SauravKumarMehta:如果这就是占用空间大的原因,那么我就不必担心了,因为内存实际上是“可用的”(即使它仍然在物理内存中)因为它可以在任何必要的时候被垃圾收集。现在我们不想为第三方jar添加另一个依赖项,但看看StringInterner做了什么会很有趣吗?看看这一点,
String str3 = new StringBuilder().append(str1).append(str2).toString();