何时使用intern()方法处理字符串字面量

40

我看到很多类似这样的遗留代码:

class A {
    public static final String CONSTANT = "value".intern();
    ...
}

我不认为需要使用intern()函数,因为在Javadoc中可以读到:"所有字面值字符串和字符串类型的常量表达式都会被池化(interned)"。这是有意义的吗?也许在语言的早期版本中有些意义?


8
除了pjp之外,是否还有其他人在回答这个问题之前阅读过它? - Adamski
可能是编译时常量都被内联了吗?的重复问题。 - David Citron
4个回答

71

这是一种技术,用于确保CONSTANT实际上不是常量。

当Java编译器看到对final static primitive或String的引用时,它将该常量的实际值插入使用它的类中。如果您然后更改定义类中的常量值但不重新编译使用类,则它将继续使用旧值。

通过在“常量”字符串上调用intern(),编译器不再认为它是静态常量,因此使用类将在每次使用时实际访问定义类的成员。


JLS 引用:


1
我刚刚通过实验证实了这一点,但是否有JLS引用? - Josh Lee
这类似于https://dev59.com/t3RC5IYBdhLWcg3wMeHf - pjp
3
我想人们不应该更改对其他类可见的常量的值。 - pjp
这是一个不错的技巧,但看起来很奇怪。有更好的技巧来完成同样的事情吗? - irreputable
2
方案A:在字符串上调用toString()方法。这可能比intern()方法更快。方案B:一个实用方法:String str(String s){ return s; } 方法应该被注释为其目的 - 打破编译时常量,以便读者在看到 static final String ABC = str("xxx"); 时明白正在发生什么。 - irreputable

16

intern() 函数在使用常量字符串时是浪费时间的,因为根据 The Java® Language Specification3.10.5 节 String Literals,字面值已经被池化了。

以下摘自Java SE 8版本:

此外,字符串文字始终引用 String 类的相同实例。 这是因为字符串字面量(更一般地说,是常量表达式的值 §15.28)会被 "interned",以共享唯一实例,使用 String.intern 方法。

我猜编码者没有意识到这个事实。

编辑:

kdgregory所指出的,这个常量可能会对内联有影响。

1- https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5


3
JLS提到了结果,但并不清楚这种折叠是在编译时发生的,还是仅仅是编译时折叠和连接然后在运行时进行内部化之间没有可观察的差异。通过字节码检查可以确定两个字符串被连接后是否在类文件中变成一个。 - seh

8
不久前,我对来自类文件(用于类文件解析器)的所有字符串进行了intern()操作。 Intern()操作使程序使用的内存减少了(这种情况下不会,因为其他人已经指出),但它确实显著减缓了程序的速度(我认为它需要4秒才能解析所有rt.jar,并且这个更改导致时间超过了8秒)。当时(我想是JDK 1.4),我研究了一下intern()代码,发现它相当丑陋,也比需要的慢。
如果我考虑在我的代码中调用intern(),我首先会对未使用intern()的代码进行性能剖析,然后对使用intern()的代码进行内存和速度的性能剖析,并查看哪一个“更差”。

3
哇...准确的信息为什么被踩了?提供的信息是错误的吗? - TofuBeer
事实上,PJP 在这个问题上的踩票还是相当慷慨的。 - Gregory Pakosz
4
我不在意负评,只关心它们的原因 :-) - TofuBeer
而且它会对PermGen空间造成重大负载,这也是需要考虑的。 - Nrj

0

我使用intern()进行“锁定”。例如,假设我有一个“交易记录”“存储库”。当我编辑和更新一项交易时,我希望锁定该交易;我可能会选择锁定tradeId.intern(),这样我就不必担心交易克隆四处漂浮。我不确定每个人是否都喜欢这种用法。

这假设id字段不太可能意外与另一个域对象的id字段发生冲突——例如,tradeId不会碰巧与account_number发生冲突,在这种情况下,也可以执行操作。

synchronized(account.getAccountNumber().intern()) {...}

查看示例


Scala的符号不就是在执行String.intern()操作吗? - Ustaman Sangat
1
问题在于它会将“交易”添加到内部存储中,并且永远不会删除,可能导致大量的内存浪费(除非您经常使用它)。 - Matthieu
1
我认为在现代JVM中,String.intern()池是可垃圾回收的。但是,是的,通常会依赖于一些持久化分布式锁定手段。 - Ustaman Sangat
请查看此文章:http://java-performance.info/string-intern-in-java-6-7-8/。 - Matthieu
然后,在另一个地方,一位同样聪明的开发人员决定使用相同的技巧来处理不同的属性,例如车牌号码、社会安全号码或任何可能具有相同内容值的字符串类型。这会导致完全不同的事物(性能下降)甚至某些相关联的事物(死锁呼叫)被锁定在相同的键上。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接