String类型的JVM层面原理总结(字符串常量池-StringTable、运行时常量池、intern、JVM的ldc)

首先我们来看下图中的代码,你看你是否能够完全答对吗?如果想要完全答对,那就要理解我正文所总结的原理;

public class TestString {
    public static void main(String[] args) {
        String s1 = "2";
        String s2 = "2";
        String ss2 = new String("2");
        String s3 = s1 + "2";
        String s4 = s1 + "2";
        String s5 = s4.intern();
        String ss = s3.intern();
        String s = "22";
        String s6 = "2" + "2";
        String ss3 = new String("3");
        String ss4 = ss3.intern();
        System.out.println(s1 == s2);//true
        System.out.println(s1 == ss2);//false
        System.out.println(s3 == s4);//false
        System.out.println(s4 == s5);//true
        System.out.println(s5 == s6);//true
        System.out.println(ss3 == ss4);//false
        System.out.println(s == s3);//false
        System.out.println(s == s4);//true
        System.out.println(s == s5);//true
        System.out.println(s == s6);//true
        System.out.println(ss == s3);//false
        System.out.println(ss == s4);//true
    }
}

引用的文章:

常量池、运行时常量池和字符串常量池(StringTable)之间的关系及StringTable经典面试题详解

字符串常量池 intern方法

看了这篇文章,我搞懂了StringTable

java ldc指令

jvm源码解读--11 ldc指令的解读

这5篇文章结合起来 基本可以验证刚才得出的结论了

  1. 虚拟机中维护的StringTable(类似于哈希表)存在于运行时常量池,key是字符串字面量的hashcode,value就是链表,存放的是对象的引用,可以理解为字符串常量池本质就是StringTable了,当然也可以把StringTable记录的引用地址指向的实例集合 理解成字符串常量池;(不管1.6和1.7及以后,其实都可以这么理解,只不过1.6字符串常量实例在方法区,1.7及以后字符串常量实例在堆内存中);

  1. class文件中的静态常量池中的字符串字面量,什么时候生成的String对象实例呢?针对于String类型是懒创建机制,是代码跑到那一行的时候才会生成;首先在类加载的时候会将各种字面量都加载到运行时常量池(除了String类型,其他基本都创建相应的实例了),这其中肯定包括方法名、字段名、类名、字符串字面量等各种常量,在执行到类似于s = "abc"或者new String("abc")这种代码的时候,字节码中会有ldc指令(虽然看官方的Java虚拟机规范中对该指令的描述比较简单,就是将运行时常量池的引用或者值,push到操作数栈,但是实际上,JVM源码针对String类型却做了很多逻辑,这也验证了我的猜想,当然这是HotSpot虚拟机的实现,Java虚拟机规范并不会约束具体某款虚拟机的实现细节),该指令会根据运行时常量池中的字符串字面量,到StringTable中寻找是否存在,如果存在就直接返回引用,如果不存在就创建String对象,然后将字符串字面量为key,对象引用为value存入StringTable中;

  1. 对于String的intern方法,是1.6和1.7及以后的区别就在字符串常量池不存在的情况下,1.6在永久代(方法区)创建String对象,放入StringTable中,1.7及以后是在堆内存创建对象,直接将当前对象放入StringTable中,最终返回的都是字符串常量池中的地址,也就是StringTable中的引用;

有疑问的地方欢迎交流

你可能感兴趣的:(java,JVM,jvm,java,开发语言)