一个关于String的疑问

java里,String是本质还是一个immutable的对象,其值具体是由String类里的一个final char数组表示

private final char value[];

看了JDK源码才发现定义一个数组可以char[] value,也可以char value[]

参考:https://javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html

1

当执行:


一个关于String的疑问_第1张图片
image.png

内存引用关系是这样的:


一个关于String的疑问_第2张图片
image.png

2

当执行:


一个关于String的疑问_第3张图片
image.png

内存引用关系是这样的:


一个关于String的疑问_第4张图片
image.png

3

当执行:


一个关于String的疑问_第5张图片
image.png

内存引用关系是这样的:


一个关于String的疑问_第6张图片
image.png

而且文中关于垃圾收集:

Unlike most objects, String literals always have a reference to them 
from the String Literal Pool. That means that they always have a 
reference to them and are, therefore, not eligible for garbage collection. 

不同于大多数对象,String总有一个来自于String常量池(String Literal Pool?)的引用,这意味着,String对象们总有一个引用持有着他们,因此不能被垃圾回收。
ok,既然定义一个不同的String就会在常量池中创建一个对堆中string的引用,并使得这个string对象很大程度上不被垃圾回收。

那么问题来了:不断地创建不同的String,是不是就撑爆了heap?:

    static int i;
    public static void test(){
        String c = "hello worldddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
        while (true){
            String b = c+i;
            i++;
            System.out.println(b);
        }

    }

很明显我尝试了,并没有,执行到2亿次,也没有..
使用visualJVM来查看:

一个关于String的疑问_第7张图片
image.png

查看metaspace,metaspace是jdk1.8之后增加的一个非heap的空间。
参考:https://segmentfault.com/a/1190000012577387
方法区移动到metaspace,constant pool似乎在heap?
一个关于String的疑问_第8张图片
image.png

不管是heap,还是metaspace,内存似乎并没有显著平稳增加。
那么在进行字符串拼接的时候(“+”号运算符),到底发生了什么,到底有没有创建新的字符串到constant pool?

一个关于String的疑问_第9张图片
image.png

Debug一下,总所周知,“+”号拼接字符串其实被编译为了StringBuilder进行拼接,debug结果显示的也是如此。看来采用StringBuilder并没有在常量池里创建多个String对象... 真的是如此吗?dump heap查看一下
首先用jps工具查出进程的vmid:
一个关于String的疑问_第10张图片
image.png

(这里发现jdk提供的工具检测还是挺多的,还有jstat 查看jvm的各种状态,一个简单用法是jstat -snap vmid 见R大blog:http://rednaxelafx.iteye.com/blog/796343)
vmid为5430,接着dump heap:
image.png

ok,分析一下hprof文件,看看到底有多少String:
一个关于String的疑问_第11张图片
image.png

可以看到拼接了快750w次,String还只有1000个,其他都是jvm的路径之类的String,真正存在在内存里的似乎只有最后一个被拼接的String。。

看来在进行拼接的时候还是进行了某些特殊的处理,代码有些分析不出来个所以然,猜测可能是jit在运行时有逃逸分析的优化?
所以我上面的测试代码可能就并没有不断的创建新的对象。
栈上分配,同步消除,标量替换
要知道经过标量替换,就不会生成新的对象了...
抑或是别的原因? 有时间再来验证。

你可能感兴趣的:(一个关于String的疑问)