【问题标题】:Why String Deduplication when we have String Pool当我们有字符串池时为什么要进行字符串重复数据删除
【发布时间】:2017-03-20 18:52:26
【问题描述】:
String De-duplication:
字符串在任何应用程序中都会消耗大量内存。每当垃圾收集器访问 String 对象时,它都会记录 char 数组。它获取它们的哈希值并将其与对数组的弱引用一起存储。一旦它找到另一个具有相同哈希码的字符串,它就会逐个字符地比较它们。如果它们也匹配,一个字符串将被修改并指向第二个字符串的字符数组。然后第一个 char 数组不再被引用,可以被垃圾回收。
字符串池:
java 程序使用的所有字符串都存储在这里。如果两个变量被初始化为相同的字符串值。内存中不会创建两个字符串,内存中只会存储一个副本,并且都指向同一个内存位置。
所以 java 已经通过检查字符串是否存在于字符串池中来确保不在堆中创建重复的字符串。那么字符串去重的目的是什么?
如果有如下代码
String myString_1 = new String("Hello World");
String myString_2 = new String("Hello World");
即使它们相同,也会在内存中创建两个字符串。我想不出除此之外字符串重复数据删除有用的任何场景。显然我一定错过了什么。我错过了什么?
提前致谢
【问题讨论】:
标签:
java
string
garbage-collection
g1gc
【解决方案1】:
字符串池仅应用于显式添加的字符串,或在应用程序中用作常量。它确实不适用于在应用程序生命周期内动态创建的字符串。但是,字符串重复数据删除适用于所有字符串。
【解决方案2】:
编译时间与运行时间
字符串池是指编译时已知的字符串常量。
如果您碰巧在运行时检索(或构造)相同的字符串一百万次,那么字符串重复数据删除将对您有所帮助,例如从文件、HTTP 请求或任何其他方式读取它。
【解决方案3】:
字符串重复数据删除享有String 内置的额外间接级别:
- 使用字符串池,您只能为两个相同的字符串返回相同的对象
- 字符串重复数据删除让您有多个不同的
String 对象共享相同的内容。
这意味着消除了创建时重复数据删除的限制:您的应用程序可以继续创建具有相同内容的新 String 对象,同时使用非常少的额外内存,因为字符串的内容将被共享。这个过程可以在一个完全不相关的时间表上完成——例如,在后台,而您的应用程序不需要太多的 CPU 资源。由于String 对象的标识不会改变,重复数据删除可以完全隐藏在您的应用程序中。
【解决方案4】:
只是为了补充上面的答案,在较旧的 VM 上,字符串池不是垃圾收集(现在已经改变,但不要依赖它)。它包含在应用程序中用作常量的字符串,因此总是需要的。如果您不断地将所有字符串放入字符串池中,您可能会很快耗尽内存。最重要的是,重复数据删除是一个相对昂贵的过程,如果您知道您只需要很短的时间段内的字符串,并且您有足够的内存。
由于这些原因,字符串不会自动放入字符串池中。你必须通过调用string.intern() 来明确地做到这一点。
【解决方案5】:
来自文档:
"初始化一个新创建的String对象,使其代表
与参数相同的字符序列;换句话说,
新创建的字符串是参数字符串的副本。除非一个
需要原始的显式副本,此构造函数的使用是
不必要,因为字符串是不可变的。"
所以我的感觉是,String 类中的这个构造函数通常不需要像你上面使用的那样。我想提供构造函数只是为了完整性,或者如果您不想共享该副本(现在有点不必要,请参阅我在说的here),但其他构造函数仍然有用,例如从字符数组等等..
【解决方案6】:
我想不出除此之外字符串重复数据删除有用的任何场景。
另外一种(更常见的)场景是使用StringBuilders。在StringBuilder类的toString()方法中,很明显是在内存中新建了一个实例:
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);
}
...
}
在严重依赖此的应用程序中,字符串重复数据删除可能会减少内存使用量。