JVM 常量池

Class文件常量池

每个Class文件都会有一个单独的常量池,我们称为Class文件常量池,我们可以用javap命令反汇编Class文件,可以查看java编译器为我们生成的字节码。

CLass文件常量池存下内容:

字面量:

  • 文本字符串(代码中用双引号包裹的字符串部分的值)
  • 被声明为finnal的常量
  • 基本数据类型的值
  • 其他  

符号引用:

  • 类符号引用:类的完全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

运行时常量池

Class文件中含有类的版本,字段,方法,接口等信息,当类被加载的时候这部分信息将会被存放到运行时常量池中,并把里面的符号地址转化为内存真实地址。运行时常量池就是将字节码文件中的信息放入方法区中,属于方法区的一部分。运行时常量池是类在加载阶段完后,将class常量池中的符号引用转存到运行时常量中;类在解析阶段后,会将符号引用转为直接引用,与字符串常量池中的引用保持一致。每个类都有一个运行时常量池,可以用来动态获取类信息。

String常量池

并且String常量池底层是用C++的HashMap实现的,key是字符串的字面量,value是字符串对象的引用(HOTSPOT)。

当String类直接赋值时,如果常量池内存在这个字符串,则s1直接指向常量池的地址,若没有,则先在常量池内创建这个字符串对象,s1直接指向常量池这个字符串的内存地址; 当String类使用new实例对象时,首先在堆里创建这个对象,若是常量池内没这个字符串,则也创建一个,然后堆里的对象的value指向常量池内的字符串。
 

public static void main(String[] args){
    String s1="hello";
    String s2="hello";
    String s3=new String("world");
    String s4=new String("world");
}

注:引用网上csdn博客图片

JVM 常量池_第1张图片

 所以以下案例为false

        String s1="a";
        String s2=new String("a");
        System.out.println(s1==s2);//false

以下看综合案例

        String s1="a";
        String s2=new String("b");
        String s3="a"+"b";
        String s4=s1+s2;
        String s5="ab";
        String s6=s4.intern();


        System.out.println(s3==s4);//false
        System.out.println(s3==s5);//true
        System.out.println(s3==s6);//true

        String x2=new String("c")+new String("d");
        String x1="cd";
        x2.intern();
        System.out.println(x1==x2);//false

String s3="a"+"b":

编译器会自动优化,组合这两个字面量变成“ab”,之后这个字面量“str1str2”和在堆中创建的String对象的引用会被放到String常量池内。所以s3==s5为true。

String s4=s1+s2:

编译器内部对于String字符串变量拼接,会创建一个StringBuilder,对于每一个要拼接的内容,调用append进行添加,最后在使用toString()方法返回成字符串。StringBuilder的value是同对象一起存放在堆中,那么拼接后的字符ab字符串value也是在堆中,并没有被添加到常量池;s4是在堆中,而s3是在常量池中,所以s3==s4为false

String.intern()

 jdk1.6 中,将这个字符串对象尝试放入串池。
• 如果字符串常量池中有,则并不会放入。返回已有的串池中的对象的地址。
• 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。
Jdk1.7起,将这个字符串对象尝试放入串池
• 如果字符串常量池中有,则并不会放入,返回已有的串池中的对象的地址。
• 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。

s3==s6:

当s6=s4.intern()的时候常量池中有ab字符串,故不放入常量池,s4依旧是堆对象,而s6指向的是常量池中的对象,故为true。

注:

在JDK1.7以前,字符串常量池 和 运行时常量池都存放在方法区中,对方法区的实现成为永久代

 在JDK1.7,字符串常量池 从方法区转移到了堆中,运行时 常量池还是在方法区中

 在jdk1.8以后,取消了永久代,取而代之的是元空间。运行时常量池和静态常量池都存放在元空间中,而字符串常量池依然在堆空间中(只是和堆共享空间,但和堆互相隔离)

StringBuilder最后也用到了new String,但为什么没有入池 ?
通过new String(“ab”) 创建对象时,是通过双引号修饰的字面量来创建的,而 StringBuilder() 拼接的字符串,不会进入字符串常量池。可以理解为,在代码中写的字符串字面量才会进入字符串常量池
 

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