什么是string实习?

什么是String在Java中的实际使用,为什么?

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern();

基本上对一系列string做String.intern()将确保所有具有相同内容的string共享相同的内存。 所以,如果你有名字的列表,其中'约翰'出现1000次,通过实习你确保只有一个'约翰'是实际分配的内存。

这可以减less程序的内存需求。 但请注意,caching是由永久内存池中的JVM维护的,与堆相比,这个池的大小通常是有限的,因此如果没有太多重复值,则不应使用intern。


更多关于使用intern()的内存限制

一方面,你可以通过内部化来删除String重复。 问题是内部化的string转到了永久代,它是为非用户对象(如类,方法和其他内部JVM对象)保留的JVM的一个区域。 这个区域的大小是有限的,通常比堆小得多。 在string上调用intern()会将其从堆中移出到永久生成中,从而有可能会耗尽PermGen空间。

– 来自: http : //www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html


从JDK 7(我的意思是在HotSpot中),事情已经改变了。

在JDK 7中,internedstring不再分配在Java堆的永久生成中,而是分配在Java堆的主要部分(称为年轻人和老一代)中,以及由应用程序创build的其他对象。 这种改变将导致更多的数据驻留在主Java堆中,永久代中的数据更less,因此可能需要调整堆大小。 由于这种变化,大多数应用程序在堆使用中只会看到相对较小的差异,但是加载很多类或大量使用String.intern()方法的较大应用程序将会看到更显着的差异。

– 从Java SE 7的function和增强

更新:Internedstring存储在Java 7之后的主堆中。 http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

有一些“吸引人的面试”问题,为什么你得到

 String s1 = "testString"; String s2 = "testString"; if(s1 == s2)System.out.println("equals!"); 

如果你应该比较string你应该使用equals() 。 以上将打印等于,因为testString已经由编译器为你编写了。 你可以使用实习方法自己实习自己的琴弦,如前面的答案所示。

JLS

JLS 7 3.10.5对其进行了定义并给出了一个实际的例子:

而且,一个string文字总是引用类String的同一个实例。 这是因为string文字 – 或者更一般地说,是常量expression式(§15.28)的值的string – 被“interned”,以便使用方法String.intern共享唯一的实例。

例3.10.5-1。 string文字

由编制单位(第7.3节)组成的计划:

 package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; } 

和编制单位:

 package other; public class Other { public static String hello = "Hello"; } 

产生输出:

 true true true true false true 

JVMS

JVMS 7 5.1说 ,实习是通过一个专门的CONSTANT_String_info结构神奇而高效地实现的(不同于大多数具有更多通用表示的对象):

string文字是对类String的一个实例的引用,并且是从类或接口的二进制表示中的CONSTANT_String_info结构(§4.4.3)派生的。 CONSTANT_String_info结构给出了构成string文字的Unicode代码点序列。

Java编程语言要求相同的string文字(即包含相同的代码点序列的文字)必须引用同一类String(JLS§3.10.5)的实例。 此外,如果在任何string上调用String.intern方法,则结果是对同一个类实例的引用,如果该string显示为文字,则会返回该实例。 因此,以下expression式必须具有真值:

 ("a" + "b" + "c").intern() == "abc" 

为了派生string文字,Java虚拟机检查由CONSTANT_String_info结构给出的代码点序列。

  • 如果方法String.intern先前在包含与CONSTANT_String_info结构相同的Unicode代码点序列的String类实例上调用,则string文字派生的结果是对类String相同实例的引用。

  • 否则,将创build一个包含由CONSTANT_String_info结构给出的Unicode代码点序列的类String的新实例; 该类实例的引用是string文字派生的结果。 最后,调用新的String实例的intern方法。

字节码

让我们反编译一些OpenJDK 7字节码来看看实际运行。

如果我们反编译:

 public class StringPool { public static void main(String[] args) { String a = "abc"; String b = "abc"; String c = new String("abc"); System.out.println(a); System.out.println(b); System.out.println(a == c); } } 

我们有不断的池:

 #2 = String #32 // abc [...] #32 = Utf8 abc 

main

  0: ldc #2 // String abc 2: astore_1 3: ldc #2 // String abc 5: astore_2 6: new #3 // class java/lang/String 9: dup 10: ldc #2 // String abc 12: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V 15: astore_3 16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 19: aload_1 20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 23: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 33: aload_1 34: aload_3 35: if_acmpne 42 38: iconst_1 39: goto 43 42: iconst_0 43: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 

请注意:

  • 03 :加载相同的ldc #2常量(文字)
  • 12 :创build一个新的string实例(以#2作为参数)
  • 35 :将ac作为常规对象与if_acmpne进行比较

常量string的表示在字节码上非常神奇:

  • 它有一个专用的CONSTANT_String_info结构,不像常规对象(例如new String
  • 该结构指向包含数据的CONSTANT_Utf8_info结构 。 这是表示string的唯一必要数据。

而上面的JVMS报价似乎表示,只要Utf8指向的是相同的,则相同的实例由ldc加载。

我已经做了类似的testing领域,并且:

  • static final String s = "abc"通过ConstantValue属性指向常量表
  • 非final字段没有该属性,但仍可以使用ldc进行初始化

结论 :对string池有直接的字节码支持,并且内存表示是有效的。

奖金:比较那整体池 ,没有直接的字节码支持(即没有CONSTANT_String_info模拟)。

什么是stringintern()?

String Interning是一种只存储每个不同string值的一个副本的方法,它必须是不可变的。 如果它是由new关键字创build的,它可以用来从池内存中返回string。

在Java中,String类有一个公共方法intern(),它返回string对象的规范表示。 Java的String类私有维护一个string池,其中string字面自动实现。

当在String对象上调用intern()方法时,它将查找池中由此String对象包含的string,如果在那里findstring,则返回池中的string。 否则,将此String对象添加到池中,并返回对此String对象的引用。

intern()方法通过查看预先存在的string文字池来帮助比较两个String对象与==运算符,它比equals()方法快。 Java中的string池是为了节省空间和更快的比较而保留的。我推荐使用equals()而不是==来比较两个string。 这是因为==运算符比较内存位置,而equals()方法比较存储在两个对象中的内容。

为什么和何时使用实习生?

虽然Java自动默认实现了所有的string,但要记住,我们只需要在不是常量的情况下实习string,而且我们希望能够将它们与其他string进行快速比较。 intern()方法应该用于用新的String()构造的string,以便通过==运算符进行比较。

让我们来看看下面的程序来理解intern()的行为

 public class TestIntern{ public static void main(String args[]){ String s1 = "Hello World"; String s2 = "Hello World"; String s3 = new String("Hello World"); final String s4 = s3.intern(); //The intern() method returns string from pool, now s4 will be same as s1,s2 System.out.println(s1 == s2);// The == operator compares references not values System.out.println(s2 == s3); System.out.println(s3 == s4); System.out.println(s1 == s3); System.out.println(s1 == s4); System.out.println(s1.equals(s2)); //The equals() method compares the original content of the string. //It compares values of string for equality System.out.println(s2.equals(s3)); System.out.println(s3.equals(s4)); System.out.println(s1.equals(s4)); System.out.println(s1.equals(s3)); } } //output true false false false true true true true true true 

来源= https://en.wikipedia.org/wiki/String_interning

stringinterning是编译器的一种优化技术。 如果在一个编译单元中有两个相同的string文字,那么生成的代码将确保在该程序集内只为该文本的所有实例(用双引号括起来的字符)创build一个string对象。

我来自C#的背景,所以我可以通过举一个例子来解释:

 object obj = "Int32"; string str1 = "Int32"; string str2 = typeof(int).Name; 

输出以下比较:

 Console.WriteLine(obj == str1); // true Console.WriteLine(str1 == str2); // true Console.WriteLine(obj == str2); // false !? 

注1 :对象通过引用进行比较。

注2 :typeof(int).Name是通过reflection方法计算的,所以在编译时不会被评估。 这些比较是在编译时进行的。

结果分析: 1)是真的,因为它们都包含相同的文字,所以生成的代码将只有一个引用“Int32”的对象。 见注1

2)是真的,因为两个值的内容都被检查了哪一个是一样的。

3)FALSE,因为str2和obj不具有相同的文字。 见注2