精华帖 (0) :: 良好帖 (4) :: 新手帖 (4) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-04-08
最后修改:2012-04-09
为了性能和内存资源上的考虑,JVM对String类型的数据做了特殊的处理。也就是大家都知道的String是immutable的或者是constant。其实很多面试或者笔试里面都会考到。有时觉得没事找事,确实,对于一般的系统,不需要考虑到底创建了几个string,只管用就是,似乎JVM会处理,至少会GC他们的。其实不然。对于大型项目,或者运行很长时间的项目,或者使用到大量字符串的项目来说,String的开销就不得不考虑。 1. String Literal Constant Pool 引用 By literal we mean any number, text, or other information that represents a value. This means what you type is what you get.
literal,就是字符串,数字等值本身。当你在编码的时候写下一个值的时候,比如10,或者“abc”你就写了一个literal。 JVM 维护了一个String Literal Pool,用来存储String Literal。 引用 A string literal consists of zero or more characters enclosed in double quotes.
A string literal is a reference to an instance of class String. Moreover, a string literal always refers to the same instance of class String. 字符串字是一个由双引号括起来的字符序列。它是指向一个String实例的引用。 比如 String s = “abc”。对于“abc”,它首先是一个String literal,它在创建之后,立即利用String的intern方法,装入到了Literal Pool并返回这个String 实例的引用给s。当你再次写abc”的时候,intern方法会先查看是否已有这个literal了,就会直接返回这个literal对应的String实例的引用。也就是说你循环1000变,内存里面也只有这么一个String literal以及他对应的String的实例。String,本质上是一个Java 类,它的实例除了包含value,也就是这个字符序列外,还有其他的属性和方法。这样看来String Object和Literal本质上是不同的。 其实除了String Literal,还有其他类型的Literal: 引用
只是,只有String Literal有这么一个pool,用来提高性能和节约内存。提高性能,是因为你可以重用已经有的String Object,这样也就节约了内存。 再来看看 new String("abc"), 只有用了双引号,就会涉及到string literal,它的逻辑就是先查看是否已有这个literal,有就返回它的string object 引用,没有就创建一个,并生成一个string的object,然后把这个object的引用返回。可见,没有string literal有且仅有一个string object与之对应。回到这句话,new String("abc"),只有用到new,就会新建一个object,这里是新建一个string object。这就都明白了吧。看一个例子: package jdk.lang; public class StringTest { public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; System.out.println(s1 == s2); } } 输出 true。也就是只生成了一个对象。 2. 编译时计算和运行时计算的区别 看下面: package jdk.lang; public class StringTest { public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; String s4 = "abc" + "abc"; String s5 = "ab" + "cabc"; System.out.println(s3 == s4); System.out.println(s4 == s5); } } 输出:false true。上面的程序,"abc" + "abc"是常量运算表达式constant expression。编译器就可以计算出值了,这就是编译时计算。同时这个表达式会被看做是string literal。但是:String s3 = s1+s2;那么s3的值就只能在运行的时候才能确定,这就是运行时计算。可见运行时会创建一个新的String,并且不会被当成string literal。 3. Intern 下面是String的intern方法的注释和签名,可见是一个native方法。 引用 A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. All literal strings and string-valued constant expressions are interned. public native String intern(); String提供了方法Intern让我们把显示的调用来把String对象放入到literal pool里面并返回这个新的引用。 package jdk.lang; public class StringTest { public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; String s4 = s3.intern(); String s5 = "ab" + "cabc"; System.out.println(s4 == s5); System.out.println(s3 == s5); System.out.println(s3.intern() == s3); System.out.println(s3.intern() == s4); System.out.println(s5.intern() == s5); } } 输出: true false false true true。s4是一个新的引用,这个引用和s5一样,但是和s3不同。也就是说,intern的时候创建了一个新的对象。但是不是每次都新建一个,只要有了,就会返回存在的。最后两个结果就说明了这点。 4. 垃圾回收 GC是不会收集Literal Pool的。但是会收集没有intern的String 变量对象。对上面的例子,s3会被回收,s4就不会。 5. constant expression - 常量表达式 引用 Compile-time constant expressions of type String are always "interned" so as to share unique instances, using the method String.intern.
常量表达式,比如 "ab" + "cabc"会被当成literal,也就等同"abcabc"。 6. 关于其他的Literal呢? 引用 If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2. 也就是说,他们是部分重用的。看一个例子: private static void IntegerLiteral() { Integer i1 = 6; Integer i2 = 6; Integer i3 = 3 + 3; Integer i4 = 3 * 2; System.out.println(i1 == i2);//true System.out.println(i3 == i2);//true System.out.println(i4 == i2);//true Integer i5 = 128; Integer i6 = 128; System.out.println(i5 == i6);//false } private static void BoolearnLiteral() { Boolean b1 = true; Boolean b2 = true; System.out.println(b1 == b2);//false } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-04-08
这文章写得很通俗易懂
学到了 intern |
|
返回顶楼 | |
发表时间:2012-04-09
写的不错,继续努力
|
|
返回顶楼 | |
发表时间:2012-04-09
最后修改:2012-04-09
详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); |
|
返回顶楼 | |
发表时间:2012-04-09
freish 写道 详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); 这个的结果是false,s3.intern()在常量池 s3在堆 一篇有营养的帖子 http://rednaxelafx.iteye.com/blog/774673 |
|
返回顶楼 | |
发表时间:2012-04-09
jinnianshilongnian 写道 freish 写道 详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); 这个的结果是false,s3.intern()在常量池 s3在堆 一篇有营养的帖子 http://rednaxelafx.iteye.com/blog/774673 非也,jdk1.7 JRockit 上华丽丽的是true 详见 http://www.iteye.com/topic/1112592 |
|
返回顶楼 | |
发表时间:2012-04-09
freish 写道 jinnianshilongnian 写道 freish 写道 详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); 这个的结果是false,s3.intern()在常量池 s3在堆 一篇有营养的帖子 http://rednaxelafx.iteye.com/blog/774673 非也,jdk1.7 JRockit 上华丽丽的是true 详见 http://www.iteye.com/topic/1112592 完了 我得赶紧学习7啦 哈哈 落伍了 |
|
返回顶楼 | |
发表时间:2012-04-09
最后修改:2012-04-09
freish 写道 jinnianshilongnian 写道 freish 写道 详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); 这个的结果是false,s3.intern()在常量池 s3在堆 一篇有营养的帖子 http://rednaxelafx.iteye.com/blog/774673 非也,jdk1.7 JRockit 上华丽丽的是true 详见 http://www.iteye.com/topic/1112592 其实咱们都理解的不完全 javadoc(sun的jdk 1.6和1.7的)描述: 引用 It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true
也就是说 s.equals(t) 则 s.intern() == t.intern() s==t,则 s.intern() == t.intern() s3.intern() == s3 这个没有定义结果是什么,要看虚拟机(如我以sun hotspot 1.6来说的结果是false,而你是以(sun hotspot 和 jrockit来说的 是true) 此处引用RednaxelaFX的原话 原文http://www.iteye.com/topic/1112592?page=3#2216483 引用 为什么JRockit与新的JDK7里的HotSpot会返回true其实很简单:它们的string pool并不拷贝输进来intern()的java.lang.String实例,只是在池里记录了每组内容相同的String实例首个被intern的那个实例的引用。 老的HotSpot之所以在前面的一些实验里会返回false是因为它的string pool实现是要求要将被string pool引用的String实例放在PermGen里的,而随便造个普通的String实例不在PermGen里,所以这里会在PermGen内创建一个原String的拷贝 。这样的话,每组内容相同的String实例首个被intern的那个实例不一定是最终放到string pool里的那个实例,自然就使得前面实验的str.intern() != str了。 使用“Java(TM) SE Runtime Environment (build 1.7.0-b147)”测试如下代码的 String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; String s4 = s1 + s2; System.out.println(s3.intern() == s3);//true System.out.println(s3.intern() == s4);//false “只是在池里记录了每组内容相同的String实例首个被intern的那个实例的引用” 所以s3.intern()==s4 false |
|
返回顶楼 | |
发表时间:2012-04-09
jinnianshilongnian 写道 freish 写道 jinnianshilongnian 写道 freish 写道 详解。。原来是标题党
下面这几句,楼主说说结果是什么呢? String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; System.out.println(s3.intern() == s3); 这个的结果是false,s3.intern()在常量池 s3在堆 一篇有营养的帖子 http://rednaxelafx.iteye.com/blog/774673 非也,jdk1.7 JRockit 上华丽丽的是true 详见 http://www.iteye.com/topic/1112592 其实咱们都理解的不完全 javadoc(sun的jdk 1.6和1.7的)描述: 引用 It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true
也就是说 s.equals(t) 则 s.intern() == t.intern() s==t,则 s.intern() == t.intern() s3.intern() == s3 这个没有定义结果是什么,要看虚拟机(如我以sun hotspot 1.6来说的结果是false,而你是以(sun hotspot 和 jrockit来说的 是true) 此处引用RednaxelaFX的原话 原文http://www.iteye.com/topic/1112592?page=3#2216483 引用 为什么JRockit与新的JDK7里的HotSpot会返回true其实很简单:它们的string pool并不拷贝输进来intern()的java.lang.String实例,只是在池里记录了每组内容相同的String实例首个被intern的那个实例的引用。 老的HotSpot之所以在前面的一些实验里会返回false是因为它的string pool实现是要求要将被string pool引用的String实例放在PermGen里的,而随便造个普通的String实例不在PermGen里,所以这里会在PermGen内创建一个原String的拷贝 。这样的话,每组内容相同的String实例首个被intern的那个实例不一定是最终放到string pool里的那个实例,自然就使得前面实验的str.intern() != str了。 使用“Java(TM) SE Runtime Environment (build 1.7.0-b147)”测试如下代码的 String s1 = "abc"; String s2 = "abc"; String s3 = s1 + s2; String s4 = s1 + s2; System.out.println(s3.intern() == s3);//true System.out.println(s3.intern() == s4);//false “只是在池里记录了每组内容相同的String实例首个被intern的那个实例的引用” 所以s3.intern()==s4 false 意义是不大,只是看到LZ上有这么段测试代码,就拿出来说说 |
|
返回顶楼 | |
发表时间:2012-04-09
freish 写道 意义是不大,只是看到LZ上有这么段测试代码,就拿出来说说
不同的虚拟机实现不一样,但是所有虚拟机应该保证 s.equals(t) 怎s.intern()==t.intern()即可。这是大前提,其他的都是不确定的 |
|
返回顶楼 | |