字符串常量池和基本数据类型的包装类常量池

一.字符串常量池存放的地方

字符串常量池:在jdk1.6时,它存在于永久代

 

 

 jdk1.7之后:

 

 

怎么验证:思路,我们只要不断的创建字符串对象,让其内存溢出,看看报错是在哪一块内存中即可 

 /**
  * jdk6:‐Xms6M ‐Xmx6M ‐XX:PermSize=6M ‐XX:MaxPermSize=6M
 * jdk8:‐Xms6M ‐Xmx6M ‐XX:MetaspaceSize=6M ‐XX:MaxMetaspaceSize=6M
 */
public class RuntimeConstantPoolOOM{
 public static void main(String[] args) {
       ArrayList<String> list = new ArrayList<String>();
       for (int i = 0; i < 10000000; i++) {
          String str = String.valueOf(i).intern();
          list.add(str);
      }
 }

 

 二.  intern() 方法:

  

 String s1 = new String("he") + new String("llo");
 String s2 = s1.intern();
 System.out.println(s1 == s2);

在 JDK 1.6 下输出是 false,创建了 6 个对象
在 JDK 1.7 及以上的版本输出是 true,创建了 5 个对象
当然我们这里没有考虑GC,但这些对象确实存在或存在过

首先上面的所示会创建多少个对象:

在jdk中: 字面量”he“ 和”llo“ 会在字符串常量池中分别创建一个对象,而  new String("he")和new String("llo") 分别会在堆中创建一个对象,字符串相加,底层是通过StringBuilder的toString()方法实现的,因此也会在堆中创建一个对象

 

 因此, String s1 = new String("he") + new String("llo"); 这行代码就创建了5个对象

现在不同的地方在于intern()方法:在jdk1.6时,s1.intern()方法会判断字符串常量池中有没“hello”这个字面量,如果没有,就会拷贝一份字面量到常量池中,而在jdk1.7以后,如果发现常量池中没有hello,它会将堆中的hello字符串的地址拷贝到

常量池中,因此在jdk1.6中会在常量池中创建多一个对象,而jdk1.7后则不会:

 

 

 上面标红的地方要注意:下面举个例子(jdK1.8):

 

 原因:在jdk1.8后,s1.intern()方法会判断字符串常量池存不存在s1的字面量,不存在就将s1在堆中的地址放到常量池,存在就不会放,很明显,“泰中科技”这个字符串在常量池中是不存在的,因此常量池会存它的在堆中的地址引用,

而“java”这个字符串,作为关键字,肯定已经存在字符串常量池中了,因此我们再次去调用intern()放法返回的地址是别人放到堆中的java字符串,因此结果是false

 

 三:字符串常用的试题:

编译优化:字符串字面量相加,在编译期就会优化了,如下面的s2

 String s0="zhuge";  //会在常量池中创建该对象
 String s1="zhuge";  //常量池中已有该对象了
 String s2="zhu" + "ge";   //编译其就会优化成”zhuge“
 System.out.println( s0==s1 ); //true
 System.out.println( s0==s2 ); //true

 

 s2没法在编译期优化,因此在堆中创建一个新对象

 

 

 

 引用是没法在编译期优化的 如上面的b

 

 

 final修饰就是常量了

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
posted @ 2022-01-03 11:57  yangxiaohui227  阅读(117)  评论(0编辑  收藏  举报