为什么我们有字符串池时的字符串重复数据删除

时间:2017-03-20 18:52:27

标签: java string garbage-collection g1gc

String De-duplication

  

字符串在任何应用程序中消耗大量内存。每当垃圾收集器访问String对象时,它都会记录char数组。它接受它们的哈希值并将其与对数组的弱引用一起存储。一旦找到另一个具有相同哈希码的String,就会将它们与char进行比较。如果它们匹配,则会修改一个String并指向第二个String的char数组。然后不再引用第一个char数组,并且可以进行垃圾回收。

字符串池:

  

java程序使用的所有字符串都存储在此处。如果两个变量初始化为相同的字符串值。内存中没有创建两个字符串,只有一个副本存储在内存中,两个副本都指向相同的内存位置。

因此,java已经通过检查字符串池中是否存在字符串来解决不在堆中创建重复字符串的问题。那么字符串重复数据删除的目的是什么?

如果有如下代码

    String myString_1 = new String("Hello World");
    String myString_2 = new String("Hello World");

即使它们相同,也会在内存中创建两个字符串。除了这个字符串重复数据删除很有用之外,我想不出任何其他情况。显然我必须遗漏一些东西。我错过了什么?

提前致谢

6 个答案:

答案 0 :(得分:3)

字符串池仅将 应用于显式添加到其中的字符串,或者在应用程序中用作常量。 适用于在应用程序生命周期内动态创建的字符串。但是,字符串重复数据删除适用于所有字符串。

答案 1 :(得分:1)

字符串重复数据删除享有内置于String间接的额外级别:

  • 使用字符串池,您只能为两个相同的字符串
  • 返回相同的对象
  • 字符串重复数据删除可让您拥有多个不同的String个对象,共享相同的内容

这转化为消除了对创建的重复数据删除的限制:您的应用程序可以继续创建具有相同内容的新String对象,同时使用非常少的额外内存,因为字符串的内容将被共享。此过程可以在完全不相关的计划上完成 - 例如,在后台,而您的应用程序不需要太多的CPU资源。由于String对象的标识不会更改,因此可以从应用程序中完全隐藏重复数据删除。

答案 2 :(得分:1)

编译时间与运行时间

字符串池是指在编译时已知的字符串常量

如果您碰巧在运行时检索(或构造)相同的字符串一百万次,则字符串重复数据删除可以帮助您,例如从文件,HTTP请求或任何其他方式读取它。

答案 3 :(得分:1)

只是为了添加上面的答案,在较旧的VM上,字符串池垃圾收集(现在已经改变,但不要依赖于此)。它包含在应用程序中用作常量的字符串,因此总是需要它。如果您不断将所有字符串放在字符串池中,则可能会很快耗尽内存。最重要的是,重复数据删除是一个相对昂贵的过程,如果你知道你只需要很长一段时间的字符串,并且你有足够的内存。

由于这些原因,字符串不会自动放入字符串池中。您必须通过调用string.intern()明确地执行此操作。

答案 4 :(得分:1)

  

我无法想到除此之外的任何场景,其中字符串重复数据删除很有用。

另一个(更多)频繁的情况是使用StringBuilder。在toString()类的StringBuilder方法中,它清楚地在内存中创建了一个新实例:

public final class StringBuilder extends AbstractStringBuilder
                                 implements java.io.Serializable, CharSequence
{
    ...

    @Override
    public String toString() {
       // Create a copy, don't share the array
       return new String(value, 0, count);
    }

    ...

}

其线程安全版本StringBuffer也是如此:

public final class StringBuffer extends AbstractStringBuilder
                                implements java.io.Serializable, CharSequence
{
   ...

   @Override
   public synchronized String toString() {
       if (toStringCache == null) {
           toStringCache = Arrays.copyOfRange(value, 0, count);
       }
       return new String(toStringCache, true);
   }

   ...
}

在严重依赖于此的应用程序中,字符串重复数据删除可能会减少内存使用量。

答案 5 :(得分:0)

来自文档:

“初始化一个新创建的String对象,使其代表     与参数相同的字符序列;换句话说,      新创建的字符串是参数字符串的副本。除非      需要显式的原始副本,使用这个构造函数是      因为字符串是不可变的,所以不必要。“

所以我的意思是说,String类中的这个构造函数通常不像你上面使用的那样需要。我想这个构造函数仅仅是为了完整性而提供的,或者如果你不想共享那个副本(现在有点不必要,请参考我所说的here)但是其他构造函数也很有用,比如获取一个字符串来自char数组的对象等等。