从字符串到常量池,一文看懂String类设计 (3)

操作数栈中的引用弹出,并赋值给局部变量表中的1号位置元素,到这一步其实执行完了String name = "dmz"这行代码。此时局部变量表中储存着一个指向堆中字符串实例的引用,并且这个字符串实例同时也被字符串常量池引用。

第四步:

这一步我就不画图了,就是方法执行完成,栈帧弹出,非常简单。

在上文中,我多次提到了字符串常量池,它到底是个什么东西呢?我们还是分为两部分讨论

位置在哪?

用来干什么的?

字符串常量池 位置在哪?

字符串常量池比较特殊,在JDK1.7之前,其存在于永久代中,到JDK1.7及之后,已经中永久代移到了堆中。当然,如果你非要说永久代也是堆的一部分那我也没办法。

另外还要说明一点,经常有同学会将方法区,元空间,永久代(permgen space)的概念混淆。请注意

方法区是JVM在内存分配时需要遵守的规范,是一个理论,具体的实现可以因人而异

永久代是hotspot 的jdk1.8以前对方法区的实现,使用jdk1.7的老司机肯定以前经常遇到过java.lang.OutOfMemoryError: PremGen space异常。这里的PermGen space其实指的就是方法区。不过方法区和PermGen space又有着本质的区别。前者是JVM的规范,而后者则是JVM规范的一种实现,并且只有HotSpot才有PermGen space。

元空间是jdk1.8对方法区的实现,jdk1.8彻底移除了永久代,其实,移除永久代的工作从JDK 1.7就开始了。JDK 1.7中,存储在永久代的部分数据就已经转移到Java Heap或者Native Heap。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了Java heap;类的静态变量(class statics)转移到了Java heap。到jdk1.8彻底移除了永久代,将JDK7中还剩余的永久代信息全部移到元空间,元空间相比对永久代最大的差别是,元空间使用的是本地内存(Native Memory)

用来干什么的?

字符串常量池,顾名思义,肯定就是用来存储字符串的嘛,准确来说存储的是字符串实例对象的引用。我查阅了很多博客、资料,它们都会说,字符串常量池中存储的就是字符串对象。其实我们可以类比下面这段代码:

HashSet<Person> persons = new HashSet<Person>;

在persons这个集合中,存储的是Person对象还是Person对象对应的引用呢?

所以,请大声跟我念三遍

字符串常量池存储的是字符串实例对象的引用!

字符串常量池存储的是字符串实例对象的引用!

字符串常量池存储的是字符串实例对象的引用!

下面我们来看R大博文下评论的一段话:

简单来说,HotSpot VM里StringTable是个哈希表,里面存的是驻留字符串的引用(而不是驻留字符串实例自身)。也就是说某些普通的字符串实例被这个StringTable引用之后就等同被赋予了“驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例里只有一份,被所有的类共享。类的运行时常量池里的CONSTANT_String类型的常量,经过解析(resolve)之后,同样存的是字符串的引用;解析的过程会去查询StringTable,以保证运行时常量池所引用的字符串与StringTable所引用的是一致的。

​ ------R大博客

从上面我们可以知道

字符串常量池本质就是一个哈希表

字符串常量池中存储的是字符串实例的引用

字符串常量池在被整个JVM共享

在解析运行时常量池中的符号引用时,会去查询字符串常量池,确保运行时常量池中解析后的直接引用跟字符串常量池中的引用是一致的

为了更好理解上面的内容,我们需要去分析String中的一个方法-----intern()

intern方法分析 /** * Returns a canonical representation for the string object. * <p> * A pool of strings, initially empty, is maintained privately by the * class <code>String</code>. * <p> * When the intern method is invoked, if the pool already contains a * string equal to this <code>String</code> object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this <code>String</code> object is added to the * pool and a reference to this <code>String</code> object is returned. * <p> * It follows that for any two strings <code>s</code> and <code>t</code>, * <code>s.intern()&nbsp;==&nbsp;t.intern()</code> is <code>true</code> * if and only if <code>s.equals(t)</code> is <code>true</code>. * <p> * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java&trade; Language Specification</cite>. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */ public native String intern();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypdgs.html