intern()在Java 6和Java 7中performance不同
class Test { public static void main(String...args) { String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1.intern()); String s2 = "Goodmorning"; if (s1 == s2) { System.out.println("both are equal"); } } }
此代码在Java 6和Java 7中产生不同的输出。在Java 6中, s1==s2
条件返回false
而在Java 7中, s1==s2
返回true
。 为什么?
为什么这个程序在Java 6和Java 7中产生不同的输出?
看来,JDK7实习生的方式与以前不同。
我使用build 1.7.0-b147testing了它,得到了“两者相等”,但是当执行它(相同的字节码)和1,6.0_24时,我没有收到消息。
它还取决于String b2 =...
行位于源代码中的位置。 以下代码也不会输出消息:
class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; String s2 = "Goodmorning"; System.out.println(s1.intern()); //just changed here s1.intern() and the if condition runs true if(s1 == s2) { System.out.println("both are equal"); } //now it works. } }
在没有在其string池中findstring之后,似乎是intern
,将实际的实例s1插入到池中。 当s2被创build时,JVM正在使用该池,所以它获得与s1相同的引用。 另一方面,如果首先创build了s2,则该引用被存储到池中。
这可能是因为从Java堆的永久生成中移除了被删除的string。
在这里find: 重要的RFE在JDK 7中解决
在JDK 7中,internedstring不再分配在Java堆的永久生成中,而是分配到Java堆的主要部分(称为年轻人和老一代),以及由应用程序创build的其他对象。 这种改变将导致更多的数据驻留在主Java堆中,永久代中的数据更less,因此可能需要调整堆大小。 由于这种变化,大多数应用程序在堆使用中只会看到相对较小的差异,但是加载很多类或大量使用String.intern()方法的较大应用程序将会看到更显着的差异。
不知道这是一个错误,从哪个版本… JLS 3.10.5状态
显式实施计算string的结果与具有相同内容的任何预先存在的文字string是相同的string。
所以问题是如何解释,编译时间或执行时间:预先存在或不存在?
我更喜欢它在7之前实施的方式。
我们来从示例中省略不必要的细节:
class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } }
让我们把String#intern
作为一个黑盒子。 基于几个testing用例运行,我会得出如下结论:
Java 6:
如果池包含的对象等于this
,则返回对该对象的引用,否则创build新的string(等this
),放入池中,并返回对创build的实例的引用。
Java 7:
如果池包含等于this
对象,则返回对该对象的引用,否则将其放到池中,并返回this
。
Java 6和Java 7都没有打破方法的契约 。
看来,新的实习方法行为是这个bug的修复的结果: http : //bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931 。
==
比较参考。 intern方法确保具有相同值的string具有相同的引用。
String.intern方法的javadoc解释:
公共stringintern()
返回string对象的规范表示forms。
最初为空的string池由String类私有地维护。
当调用intern方法时,如果池已经包含一个与equals(Object)方法确定的String对象相等的string,则返回该string。 否则,将此String对象添加到池中,并返回对此String对象的引用。
因此,对于任何两个strings和t,当且仅当s.equals(t)为真时,s.intern()== t.intern()才为真。
所有文字string和string值的常量expression式都被禁用。 string文字在Java语言规范的§3.10.5中定义
返回:与此string具有相同内容的string,但保证来自唯一string池。
因此,如果没有实习,编译器会查看Java代码中的常量,并从中build立常量池。 有一个由String类维护的不同的池,并且interning检查传入的string对池,并确保引用是唯一的(所以==将工作)。
在jdk6中: String s1="Good";
在常量池中创build一个String对象“Good”。
s1=s1+"morning";
在常量池中创build另一个String对象“morning”,但是这次实际上是JVM: s1=new StringBuffer().append(s1).append("morning").toString();
。
现在,当new
运算符在堆中创build一个对象时,因此s1
的引用是堆而不是常量池,并且String s2="Goodmorning";
在常量池中创build一个string对象“Goodmorning”,其引用存储在s2
。
因此, if(s1==s2)
条件为false。
但是在jdk7中会发生什么?
第一种情况:
在第一个代码中,实际上是在string池中添加了三个string。 1. s1 =“好”
2. s1 =“早上好”(连接后)3. s2 =“晚上好”
在做if(s1 == s2)时,对象是相同的,但引用不同,因此它是错误的。
第二种情况:
在这种情况下,您使用的是s1.intern(),这意味着如果池已经包含一个与equals(Object)方法确定的String对象相等的string,则返回该string。 否则,将此String对象添加到池中,并返回对此String对象的引用。
- s1 =“好”
- s1 =“早上好”(连接后)
- 对于strings2 =“早上好”,新的string不会被添加到池中,并为s2获取现有的string的引用。 因此如果(s1 == s2)返回true。
你需要使用s1.equals(s2)
。 使用==
与String
对象比较对象引用本身。
编辑:当我运行你的第二个代码片段,我不会得到“两个都是平等的”打印出来。
编辑2:说明使用'=='时比较引用。
主要有4种比较string的方法:
- “==运算符”:它只是比较string对象的引用variables。 所以它可能会给你意想不到的结果,这取决于你如何创buildstring,即使用String类的构造函数,或者简单地使用双引号,因为两者都获得不同的内存(分别在堆和池中)。
- “equals(Object)method”:这是对象类的方法,由string类OVERLOADED。 它比较整个string和IS CASE SENSITIVE。
- “equalsIgnoreCase(String)方法”:这是string类的方法,比较整个string,并且不是CASE SENSITIVE。
- “比较(string)方法”:逐字符比较两个string,并返回它们的差值,如果返回值为0,则表示string相等。
每当你比较两个string之间,不要使用==
和使用eqauls eqauls()
因为你比较对象而不是引用:
string1.equals(string2);
结果代码依赖运行时:
class Test { public static void main(String... args) { String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6. } }
如果你这样写:
class Test { public static void main(String... args) { String s = "GoodMorning"; String s1 = "Good"; s1 = s1 + "morning"; System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6. } }
原因是'ldc#N'(从常量池加载string)和String.intern()都将在热点JVM中使用StringTable。 有关详细信息,我写了一篇英文文章: http : //aprilsoft.cn/blog/post/307.html
- 如果GetStringUTFChars返回一个副本,你应该调用ReleaseStringUTFChars吗?
- 使ArrayList只读
- ArrayList与LinkedList
- System.out.println()和System.err.println()之间的区别
- 实现简单的Trie来实现高效的Levenshtein距离计算 – Java
- 为什么用浮点(或双精度)数字除零不会抛出Java中的java.lang.ArithmeticException:/ by零
- 在Java中处理ResultSet的有效方法
- Hibernate CollectionOfElements EAGER获取重复的元素
- Javagenerics的types参数中的问号是什么意思?