StringTable串池的常见问题

导读:本篇文章讲解 StringTable串池的常见问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1. 为什么有字符串常量池?

字符串常用,而频繁的创建字符串对象,对性能的影响是非常大的,所以,用常量池的方式可以很大程度上降低对象创建、分配的次数,从而提升性能;
假如有大量重复的字符串,而相同的字符串只需要在串池中存一次,就可以反复被引用而不需要重复创建对象

2. 字符串常量池在哪?

字符串常量池由字符串组成 ,常量池中的字符串在【类加载时】 进入字符串常量池
JDK1.6,字符串常量池在运行时常量池中,而运行时常量池在方法区(永久代)
JDK1.7及之后,字符串常量池在

为什么要将StringTable移到堆中?
: 永久代的回收效率很低,需要Full GC才会出发永久代的垃圾回收,而Full GC会等到老年代的空间不足才会触发,出发的实际会比较晚,导致StringTable的回收效率不高;
而在JDK1.7 之后将其移到堆中,堆中只需要Minor GC就会触发垃圾回收机制

3. 以下代码创建了几个对象 ?过程如何 ?

3.1

 String s1=“abc” ;

答:创建0个或者1个;

  1. 先在栈中创建一个引用s1,然后在字符串常量池中寻找有没有“abc”对象,
  2. 若有则直接将字符串常量池中的引用赋值给栈顶的引用
    若没有,则需要在在字符串常量池创建“abc”对象(实际也是在堆中,然后将引用放到字符串常量池)

3.2

String s2=new String(“abc”);

答:1个或2 个
①用new 则会先在堆中创建了一个字符串对象,并在栈创建一个引用s2指向这个对象
②ldc命令会触发解析字符串,会将字符串引用#3在字符串常量池中去查找
如果有,不再创建对象; 如果没有,则会在字符串常量池中创建“abc”字符串(其实还是在堆中创建,只是将引用放到了字符串常量池), #3被解析成字符串对象的引用

3.3

 String s=“a"+"b"; (字符串常量拼接)

Javac在编译期优化,因为a和b都是常量,这俩拼接的结果是确定的不会再变化,所以【编译期间】会先会进行拼接成“ab”符号,所以还是创建了0个或者1个对象;

String s1=“a";
String s2=“b";
String s3=s1+s2;   (字符串变量拼接)

变量拼接 —– 底层用的都是StringBuilder的append方法实现 !

s1和s2是变量,结果不确定,所以需要在运行期间用StringBuilder来拼接:
1.先创建StringBuilder对象,
2.String s4=s1+s2 对应代码为 new StringBuilder().append(“a”).append(“b”).toStringtoString() 方法创建了String对象在中!;
所以创造了 2 个对象;

3.4

String a = new String(“a“+“b“);

创建1个或2个对象;
”a“和”b”是常量,编译器会提前将其优化为”ab”,所以和String s2=new String(“ab”);效果一样,
new String() 必然在堆中产生一个对象。
然后,若字符串常量池不存在则再创建对象。

3.5

String str = new String(“a”)+new String(“b”)

假设字符串常量池中没有”a”、“b” 对象,则共创建6个对象
在这里插入图片描述
1.new了一个StringBuilder对象(变量拼接底层用的都是StringBuilder的append方法实现)
2.第四行new了一个String对象在堆中
3.第六行ldc “a“到字符串常量池中
4.第九行new了一个Stirng对象在堆中
5.第十一行ldc “b“到字符创常量池中
6.toString 底层new String了“ab”对象在堆中

所以最多创造了6个对象!StringBuffer对象,两个a对象,两个b对象,最后一个toString生成的对象;

问:此时字符串常量池是否存在字符串”ab”?
答: 从上方的字节码中可以明显看出并没有ldc”ab”所以在字符串常量池中是没有”ab”的

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

3.6
在这里插入图片描述

  • s3==s4 ? false
    s3是常量字符串拼接,由编译器优化,提前将ab合并为一个符号,放入串池中;
    s4是变量字符串拼接,底层使用StringBuilder两次append追加然后toString即new
    String了一个字符串对象放在

  • s3==s5 ? true
    s5是字面量,首先检查常量池的内容,已经有“ab”了,s5不会创建新的对象,直接引用常量池中已有的对象,所以s3和s5是同一个对象

  • s3==s6 ? true
    s4.intern, 常量池中已经有ab, s4没能入池成功,返回了常量池中的“ab”对象作为s6
    s6也就是最早放入常量池的ab对象,即和s3是一个对象

4. 入池问题

引入:
由StringBuilder拼接生成的String对象只在堆中,不会入池

作用:
使用intern() 方法,可以主动将串池中还没有的字符串对象放入字符串常量池
在这里插入图片描述
4.1.

String s1=new String("a")+new String("b")
String s2=s1.intern();
System.out.println(s2=="ab");

true
此时ab是用StrtingBuilder拼接而成,拼接的只在堆中,不会入池
使用intern方法,如果串池中之前没有,则”ab” 对象放入串池,最后返回串池中的对象
而s2 就是返回的串池中的那个对象,而后的字面量‘ab’ ,要先检查串池,串池中已有则不会创建新的对象,所以这里的“ab”就是串池中的对象。

4.2.

String x="ab";
String s1=new String("a")+new String("b")
String s2=s1.intern();
System.out.println(s2==x);
System.out.println(s1==x);

true
false

x已经生成了“ab”对象,并放入串池
此时再尝试把堆中的“ab”放入串池,但串池中已经有“ab”了,就返回串池中的“ab”即x作为s2 ,所以s2 = x
而s1是堆中的对象,所以s1 ≠ x

5. 包装类比较

Integer x=3;   
Integer y=3;  
System.out.println(x==y);  

true
在java中,将常用数字 [-128,127] ( byte取值范围)放到了方法区的整数型常量池!与字符串常量池类似!
目的是只要用这个区间的数据则不需要new了,直接从整数型常量池中取出来即可,减小开销;

Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true

用new的方式则一定会在堆中创造Integer对象,而用“==”比较的是内存地址返回的是false,
Integer重写了equals,所以用equals是比较值,结果是true;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/89302.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!