避免使用Java中的重复字符串

时间:2011-02-22 09:05:35

标签: java string duplicates

我想问一个关于在Java中避免String重复的问题。

上下文是:带有标记和属性的XML,如下所示:

<product id="PROD" name="My Product"...></product>

使用JibX,这个XML在这样的类中被编组/解组:

public class Product{
private String id;
private String name;
// constructor, getters, setters, methods  and so on
}

该程序是一个长时间的批处理,因此创建,使用,复制了产品对象

嗯,问题是: 当我用 Eclipse内存分析器(MAT)这样的软件分析执行时,我发现了几个重复的字符串。例如,在id属性中, PROD 值在2000个实例等处重复。

我该如何避免这种情况? Product类中的其他属性可能会在执行过程中更改其值,但 id name ...等attrs不会频繁更改。

我已经了解了一些关于 String.intern()方法的内容,但我还没有使用过,我不确定它是否是一个解决方案。我可以在这些属性中定义最常用的值,例如类中的静态最终常量吗?

我希望我能以正确的方式表达我的问题。 非常感谢任何帮助或建议。提前谢谢。

5 个答案:

答案 0 :(得分:12)

如果您确实遇到问题,

实习将是正确的解决方案。 Java在内部池中存储字符串文字和许多其他字符串,每当即将创建新字符串时,JVM首先检查字符串是否已存在于池中。如果是,它将不会创建新实例,而是将引用传递给 interned String对象。

有两种方法可以控制此行为:

String interned = String.intern(aString); // returns a reference to an interned String
String notInterned = new String(aString); // creates a new String instance (guaranteed)

所以可能是,库实际上为所有xml属性值创建了新实例。这是可能的,您将无法更改它。


实习生具有全局效果。一个实习的字符串立即可用于“任何对象”(这个视图确实没有意义,但它可能有助于理解它。)

所以,我们假设我们在课程Foo中有一行,方法foolish

String s = "ABCD";

字符串文字立即被实习。 JVM检查,如果“ABCD”已经在池中,如果没有,“ABCD”存储在池中。 JVM将对实习字符串的引用分配给s

现在,可能在另一个班级Bar中,方法barbar

String t = "AB"+"CD";

然后JVM将像上面一样实习“AB”和“CD”,创建连接的String,看看,如果它已经是intered,嘿,是的,并且将对interned String“ABCD”的引用分配给{ {1}}。


调用t可能有效或失败。是的,实习字符串"PROD".intern()。但是有一个机会,jibx确实用

为属性值创建了新的字符串
"PROD"

在这种情况下, value 将不会引用一个interned String(即使String value = new String(getAttributeValue(attribute)); 在池中),而是对堆上的新String实例的引用。 / p>

而且,对于命令中的另一个问题:这只发生在运行时。编译只是创建类文件,String池是对象堆上的数据结构,由JVM使用,用于执行应用程序。

答案 1 :(得分:6)

尽管String.intern()可以通过将每个值减少到单个唯一String实例来解决该问题,但它会引入另一个问题:每个intern() - 编辑String都可以存活在JVM中很长一段时间。如果ID变化很大(即它们不是有限集合的一部分,但可以是任何值),那么从长远来看,这可能会产生巨大的负面影响。

编辑:我曾经声称intern() - ed字符串不能被GC,但是@nanda证明我错了this JavaWorld article。虽然这在一定程度上减少了intern()引入的问题,但它仍未完全删除:intern()提供的池无法控制,并且可能在垃圾收集方面产生意外结果。

幸运的是GuavaInterner接口的形式提供了一个解决方案,它是帮助类Interners:使用Interners.newStrongInterner()可以创建一个可以充当“{3}}的对象独立String个对象的池与String.intern()的对象大致相同,只是池绑定到该实例,如果丢弃池,则内容也可以符合垃圾回收的条件

答案 2 :(得分:1)

是的,实习是正确的解决方案,你已完成了你的功课(即通过探查器检查这是问题所在)。

如果存储太多,实习可能会导致问题。需要增加permgen记忆。尽管有些人说过,实际的字符串也是垃圾收集的,所以如果不再使用某些字符串,那么它将成为垃圾收集对象。

一些支持文章:

  1. 我的博客:http://blog.firdau.si/2009/01/06/java-tips-memory-optimization-for-string/
  2. 是否收集了实习生垃圾?:http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html
  3. 打破'Busting String.intern()神话':http://kohlerm.blogspot.com/2009/01/is-javalangstringintern-really-evil.html

答案 3 :(得分:0)

另一种解决方案:

您可以尝试在<xs:enumeration/>属性上定义@id限制(如果您的域模型允许这样的话)。如果JibX与JAXB或其他XML-Java映射标准一样聪明,那么这可以映射为具有常量文字的Java enum,可以大量重用。

我会尝试ID值,因为它看起来像是对我的枚举......

答案 4 :(得分:0)

众所周知,String对象可以通过两种方式创建,使用文字和new运算符。

如果您使用像String test = "Sample";这样的文字,那么这将在String对象池中缓存。因此,此处不需要实习,因为默认情况下将缓存字符串对象。

但是如果你创建一个字符串对象,比如String test = new String(“Sample”);那么这个字符串对象将不会被添加到字符串池中。所以这里我们需要使用String test = new String("Sample").intern();强制将字符串对象推送到字符串缓存。

因此,建议使用字符串文字而不是新操作符。

所以在你的情况下私​​有静态最终String id =“PROD”;是正确的解决方案。