字符串及常量池

在JDK1.7之前,运行时常量池包含字符串常量池存在方法区中,此时的hotspot虚拟机对方法区的实现在永久代。

在JDK1.7,字符串常量池从方法区拿到堆中,运行时常量池剩下的东西还在方法区中,也就是hotspot中的永久代。

在JDK1.8,hotspot虚拟机移除永久代使用元空间来代替,字符串常量池还在堆中,运行时常量池还在方法区中,方法区的实现由永久代变为元空间。

在JDK1.7中,字符串常量池StringTable为什么从永久代移到堆中

因为永久代的回收效率低,只有Full GC才会触发垃圾回收机制,导致StringTable回收效率不高,开发中会有大量字符串被创建。放到堆里能够及时回收内存。

为什么去除永久代

  • 永久代在jvm中,合适的内存大小很难确定(元空间在本地内存中,无需考虑大小)

  • 永久代调优特别困难

字符串常量池是什么

字符串的创建是比较频繁的,而字符串的分配和其他对象的分配是类似的,需要耗费大量的时间和空间,从而影响程序的运行性能,所以作为最基础最常用的引用数据类型,Java设计者在JVM层面提供了字符串常量池。JVM会先检查池,如果字符串在池中,则会返回池中的实例引用。如果字符串不在池中,则会实例化一个字符串并存放在池中。

字符串

字符串的创建的两种方式

  • 采用字面值的方式赋值
  • 采用new关键字的方式新建一个字符串对象

采用字面值赋值的方式

public class Test {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);
    }
}

结果:true

采用字面赋值的方式创建一个字符串,JVM首先会去字符串常量池中查找是否存在"abc"的这个对象,如果不存在,则在字符串常量池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给字符串常量s1,这样s1会指向池中"abc"这个字符串对象;如果存在,则不会创建任何对象,直接将字符串常量池中"abc"这个对象的地址返回,并赋给字符串常量s2,这样s2会指向池中"abc"这个字符串对象。因此s1和s2指向的是同一个对象,System.out.println(s1==s2); 返回的是true。

采用new关键字的方式

public class Test {
    public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1 == s2);
    }
}

结果:false

采用new关键字的方式创建一个字符串,JVM首先会去字符串常量池中查找是否存在"abc"这个字符串对象,如果有,则不会在池中创建"abc"这个对象,直接去堆中创建一个字符串对象"abc",然后将堆中的这个"abc"对象的地址引用返回赋值给s1,s1指向堆中创建的"abc"这个字符串对象;如果没有,首先会在字符串常量池中创建一个"abc"的字符串对象,然后在堆中创建一个"abc"的字符串对象,并将堆中创建好的"abc"字符串对象的地址引用返回赋值给s1,s1指向堆中创建的"abc"这个字符串对象。

因为采用的是new关键字创建对象,每次new出来的是一个新的对象,也就是说s1和s2指向的是不同的对象,即System.out.println(s1 == s2);返回的是false

intern()

如果字符串 s 在字符串常量池中存在对应字面量,则intern()方法返回该字面量的地址;如果不存在,则创建一个对应的字面量,并返回该字面量的地址。

public class Test07 {
    public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = "abc";
        //因为s1使用new创建对象,s1的引用是堆中的引用,而s1.intern()的引用是字符串常量池中的引用。
        System.out.println(s1 == s1.intern());  false
        //s2是直接赋值,s2的引用字符串常量池的引用,s2.intern()的引用也是常量池中的引用。
        System.out.println(s2 == s2.intern());  true
        //intern()返回的是常量池的地址,s1.intern()和s2.intern()返回引用都是同一个常量池的地址。
        System.out.println(s1.intern() == s2.intern());  true
    }
}
// 在堆中创建字符串对象”Java“
// 将字符串对象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s2 = s1.intern();
// 会在堆中在单独创建一个字符串对象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一个对象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的对象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一个对象
System.out.println(s1 == s4); //true

字符串s1在字符串常量池中创建了一个"Java"对象,并将常量池的引用赋值给s1。

字符串s2用了intern(),因为常量池中有s1创建的对象,将s1的引用赋值给s2,那么s1和s2引用指向同一个对象。

字符串s3采用new关键字创建了Java对象,因为常量池中有该对象,所以s3会在堆中创建一个"Java"对象,并将堆中的引用赋值给s3。

字符串s4使用了intern()方法,会将常量池中该对象的引用赋值给s4,最后s4和s1都是指向字符串常量池中的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值