【问题标题】:Avoid duplicate Strings in Java避免Java中的重复字符串
【发布时间】:2011-07-01 20:35:13
【问题描述】:

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

context 是:一个带有如下标签和属性的 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
}

程序是一个长时间的批处理,所以Product对象被创建、使用、复制等。

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

如何避免这种情况? Product 类中的其他属性可能会在执行过程中改变它们的值,但像 idname 等属性不会如此频繁地改变。

我已经阅读了一些关于 String.intern() 方法的内容,但我还没有使用过,我不确定它是否可以解决这个问题。我可以在类中的 static final 常量等属性中定义最常见的值吗?

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

【问题讨论】:

  • 如果您有重复的 id,您如何区分两个 Product 对象?
  • @Suresh,产品是相同的,但它在不同的时刻和/或过程中的执行中被实例化。一个请求创建产品,另一个不同的请求创建其他实例,等等。因此,这些实例包含重复的字符串 PROD,并且内存分析器检测到内存浪费。

标签: java string duplicates


【解决方案1】:
如果您真的有问题,

实习 将是正确的解决方案。 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,在方法@​​987654327@:

String t = "AB"+"CD";

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


调用"PROD".intern() 可能会成功,也可能会失败。是的,它实习生字符串"PROD"。但是有一个机会,jibx 真的为属性值创建了新的字符串

String value = new String(getAttributeValue(attribute));

在这种情况下,value 将不会引用内部字符串(即使 "PROD" 在池中),而是引用堆上的新字符串实例。

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

【讨论】:

  • @Andreas,我会尝试实习。也谢谢你
  • @Andreas_D,一个问题:String.intern() 是在类级别还是 JVM 级别实习?谢谢
  • @yaki_nuka - 池是 JVM 内部数据结构,我们无法通过字节码访问它(iaw - 使用 Java/Scala/... 程序)。没有人可以“看到”,实际上是哪些字符串被实习了。
  • @Andreas_D,好吧,我又在读了,也许我的问题格式不正确。我不想访问 JVM 数据结构。我的问题是:在 A 类中我写 "ABCD".intern() 并在 B 类中我再次写 "ABCD".intern() 。有多少个互联网字符串?阅读 JavaDoc(1.4.2 版本),我认为有一个“ABCD”实例(不是一个用于 A,另一个用于 B)。再次感谢
  • (对不起,我想写interned Strings,没有internet Strings
【解决方案2】:

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

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

幸运的是,GuavaInterner 接口的形式提供了一个解决方案,它的帮助程序类Interners:使用Interners.newStrongInterner(),您可以创建一个可以充当唯一String 对象的“池”的对象与String.intern() 的方式非常相似,只是池绑定到该实例,如果您丢弃池,则内容也可以进行垃圾回收。

【讨论】:

  • 好点。不过,我猜这对于批处理来说不一定是个问题......
  • @Lukas:如果批处理在其自己的 JVM 中启动,并且在批处理完成后停止并且批处理仅适用于单个工作单元,那么是的:这应该不是问题。但是我已经看到 JVM 运行一些调度软件来在单个长时间运行的 JVM 中运行本质上是“批处理”的东西。在这些情况下,它成为一个问题。
  • @nanda:有趣的文章。我需要经历它并可能会调整我的答案。这可能会减少问题,但不会完全消除它。
  • @Joachim:对。这就是不一定的意思:-)
【解决方案3】:

是的,实习是正确的解决方案,而且您已经完成了作业(即通过分析器检查这是问题所在)。

如果存储过多,实习可能会导致问题。 permgen 内存需要增加。尽管有人说,interned Strings 也是被垃圾回收的,所以如果一些字符串不再使用,它​​就会成为被垃圾回收的对象。

一些支持文章:

  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. 打破“打破 String.intern() 神话”:http://kohlerm.blogspot.com/2009/01/is-javalangstringintern-really-evil.html

【讨论】:

    【解决方案4】:

    另一种解决方案:

    您可以尝试在您的@id 属性上定义&lt;xs:enumeration/&gt; 限制(如果您的域模型允许这样的事情)。如果 JibX 与 JAXB 或其他 XML-Java 映射标准一样智能,那么它可以映射为具有常量字面量的 Java enum,可以大量重用。

    我会尝试使用 ID 值,因为它对我来说有点像枚举...

    【讨论】:

    • 这是个好主意,但不适合我的问题。谢谢你的回答
    【解决方案5】:

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

    如果你使用像String test = "Sample"; 这样的字面量,那么它会被缓存在字符串对象池中。所以这里不需要实习,因为默认情况下字符串对象将被缓存。

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

    所以总是建议使用字符串字面量而不是 new 运算符。

    所以在你的情况下 private static final String id = "PROD";是正确的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-11
      • 1970-01-01
      • 2014-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多