public class StringExample{
private static final String CONSTANT = "Cannon Ball!!!"; // <- In the pool
public void processStrings(String[] args){
// ^--Assumed contents are variables, not in the pool
String temp = "I'll wade in."; // <- In the pool
StringBuilder sb = new StringBuilder(100);
sb.append("I").append(" hate").append(" water.");
String dynamic = sb.toString(); // <- Not in the pool.
dynamic.intern(); // <- Now it's in the pool.
}
}
6条答案
按热度按时间zbwhf8kr1#
从this unofficial blog开始
Java虚拟机维护一个内部的字符串引用列表(唯一字符串池),以避免堆内存中出现重复的字符串对象。每当JVM从类文件加载字符串文字并执行时,它都会检查该字符串是否存在于内部列表中。如果它已经存在于列表中,则不会创建新的字符串,而是使用对现有字符串对象的引用。
JVM在内部对字符串文字执行这种类型的检查,但不对它通过'new'关键字创建的字符串对象执行这种类型的检查。可以使用
String.intern()
方法显式强制JVM对通过'new
'关键字创建的字符串对象执行这种类型的检查。这将强制JVM检查内部列表并使用现有的字符串对象(如果已存在)。envsm3lx2#
没有检查一个新字符串是否已经在字符串池中,这就是为什么你不应该使用
==
来比较字符串的原因之一。字符串池用于编译时已知的字符串。或者,如果您手动使用
intern()
并使用返回的示例。4xy9mtcn3#
每次在运行时动态创建String时,都会返回一个新的String示例。String池只包含String文本和String编译时常量,或者通过调用
String.intern()
显式添加到池中的String。rkue9o1l4#
当JVM运行时,只有常量字符串(即硬编码)在字符串池中。
所有动态字符串都在字符串池的外部。您可以通过调用
String.intern()
以编程方式将字符串添加到池中。编辑:针对您的问题提出您的意见:当您遇到类似以下情况时:
在编译时,编译器只是将引用替换为硬编码字符串,因此这等效于执行
esyap4oy5#
Java有一个字符串池,因此在实际创建一个新字符串之前,我猜会检查是否已经存在相同的字符串,这个调用的成本是多少?
这种情况只发生在字符串文本上,而不是你创建的字符串上。你可以调用String.intern()将它添加到池中,但必须在它被创建之后。
池是基于散列结构还是基于树结构?
这是一个固定大小的哈希Map,带有冲突的链表。我建议你不要过度使用它。因为它不能伸缩。
在这两种情况下,我猜归结为字符串哈希代码的性能,因为搜索树使用哈希代码作为关键字?
如果你需要一个字符串池,我建议你自己写一个。我有一个,它接受一个
byte[]
或一个CharSequence
,比如一个StringBuilder,并池化字符串,所以它在90%的时间里不会创建对象(这是目标)我猜它大约是log n,其中n是堆中当前的字符串数。
实际上是O(1),直到大约10,000,这是容量,之后它是O(n),这不是很大。
它如何池化/缓存文本,编译器是否使用对静态对象的引用来替换文本?-
编译器在给定的类文件中组合字符串文字,但JVM接受它并为整个JVM组合它们。
fcy6dtqo6#
任何在编译时已知的字符串都被池化,这意味着下面的语句只创建一个字符串。(字符串文字是最明显的例子,但任何编译时常量字符串都被池化)
但是,在程序正常运行过程中创建的任何字符串都不会进入池,因此不存在此类检查,除非您显式调用
.intern()