作者:Java中文社群
我想所有Java程序员都曾被这个newString的问题困扰过,这是一道高频的Java面试题,但可惜的是网上众说纷纭,竟然找不到标准的答案。有人说创建了1个对象,也有人说创建了2个对象,还有人说可能创建了1个或2个对象,但谁都没有拿出干掉对方的证据,这就让我们这帮吃瓜群众们陷入了两难之中,不知道到底该信谁得。
但是今天,老王就斗胆和大家聊聊这个话题,顺便再拿出点证据。
以目前的情况来看,关于newString(xxx)创建对象个数的答案有3种:
有人说创建了1个对象;有人说创建了2个对象;有人说创建了1个或2个对象。而出现多个答案的关键争议点在「字符串常量池」上,有的说new字符串的方式会在常量池创建一个字符串对象,有人说new字符串的时候并不会去字符串常量池创建对象,而是在调用intern()方法时,才会去字符串常量池检测并创建字符串。
那我们就先来说说这个「字符串常量池」。
字符串常量池
字符串的分配和其他的对象分配一样,需要耗费高昂的时间和空间为代价,如果需要大量频繁的创建字符串,会极大程度地影响程序的性能,因此JVM为了提高性能和减少内存开销引入了字符串常量池(ConstantPoolTable)的概念。
字符串常量池相当于给字符串开辟一个常量池空间类似于缓存区,对于直接赋值的字符串(Strings=xxx)来说,在每次创建字符串时优先使用已经存在字符串常量池的字符串,如果字符串常量池没有相关的字符串,会先在字符串常量池中创建该字符串,然后将引用地址返回变量,如下图所示:
以上说法可以通过如下代码进行证明:
publicclassStringExample{publicstaticvoidmain(String[]args){Strings1=Java;Strings2=Java;System.out.println(s1==s2);}}以上程序的执行结果为:true,说明变量s1和变量s2指向的是同一个地址。
在这里我们顺便说一下字符串常量池的再不同JDK版本的变化。
常量池的内存布局
从JDK1.7之后把永生代换成的元空间,把字符串常量池从方法区移到了Java堆上。
JDK1.7内存布局如下图所示:
JDK1.8内存布局如下图所示:
JDK1.8与JDK1.7最大的区别是JDK1.8将永久代取消,并设立了元空间。官方给的说明是由于永久代内存经常不够用或发生内存泄露,会爆出java.lang.OutOfMemoryError:PermGen的异常,所以把将永久区废弃而改用元空间了,改为了使用本地内存空间,