Java的String相关研究
使用工具:
vscode,编写java代码。
jvisualvm,分析虚拟机的内存,查看虚拟机内每一个类的实例数。
预备知识:
java虚拟机jvm启动时会申请一块内存,并将内存分为方法区,堆,虚拟机栈等。
- 方法区存放类信息,常量,静态变量等数据。
- 堆存放对象实例。
- 虚拟机栈的每一个栈帧用于存放局部变量表,操作数,动态链接,方法出口等信息,程序执行进入代码块或者方法时会将此栈帧入栈,而当此代码块、方法运行完毕束后会出栈。
String的对象分布在方法区的运行时常量池内的字符串常量池与堆中,而在jdk1.7之后字符串常量池被移动到了Java堆中(与运行时常量池分离)。
研究问题:
String直接赋值与new String时都发生了什么
1)直接赋值时,会先查找字符串常量池里是否存在此字符串,若存在,则将此引用直接指向字符串常量池中已经存在的字符串。若没有存在,则会在字符串常量池里新建一个此字符串,并将此引用指向该字符串。
下面用代码以及内存分析证明一下:
程序运行到第6行时暂停,这时在jvisualvm里生成dump文件(文件里面有程序当前的堆内存内的全部数据),我们可以根据字符串对象的值查询此时的内存里有多少个值为"abc"的对象。
显然,整个内存里只有1个值为abc的字符串。
2)使用new String时,第一步类似于直接赋值,先检查字符串常量池中是否有与此字符串相同的字符串,若无则在字符串常量池中创建,若有则不做操作。之后会在堆内创建一个内容与该字符串相同的String对象。也就是会在内存里产生1~2个字符串对象。
下面用代码证明一下:
这段代码里用new String新建了一个值为abc的String对象,将会在字符串常量池与堆内分别建立一个值为abc的String对象。显然,内存里有2个值为abc的字符串。
下面看这段代码,
结合之前的测试,不难推测出,运行到第六行时内存中仍会有两个值为abc的字符串
首先在第四行,会在字符串常量池与堆内存中分别建立一个值为abc的字符串。
而第5行则会将a指向字符串常量池中的abc;
接下来用jvisualvm验证一下。
显然,与上一个代码运行时同样只有两个abc;