字符串常量池

string的基本特性

  • final修饰不可被继承
  • 可被序列化
  • 可被比较大小
  • jdk9之前为char数组存放,9及之后改为byte数组存放
    • 为什么改变存储结构?一个字符占两个字节,很多情况只需要一个字节就能存储,这样造成空间浪费。采用字节数组后会记录编码方式从而决定每个字符语言的空间,节省了堆空间
  • 一经创建不可变(因为数组无法修改)
  • 直接new的字符串保存在堆中,而不是堆的字符串常量池
  • 字面量字符串存放在堆空间的字符串常量池,字面量创建的相同字符串不会重复创建
    • 底层存储结构是hashtable
    • jdk6默认长度是1009,jdk7后默认60013,可通过-XX:StringTableSize设置



      内存分配

  • 存放在字符串常量池
  • 字符串常量池在jdk6及以前存放在永久代,在7及以后存放在堆
    • 为什么要调整字符串常量池位置?永久代空间小,gc回收频率低



      字符串拼接操作

  • 编译优化:两个字面量字符串直接拼接会被直接优化为一个字符串
  • 拼接对象只要有一个是变量则存放在堆中而不是字符串常量池
    • 具体过程相当于第一步new一个StringBuilder,第二步将拼接字符串通过append拼接,第三步通过toString输出地址(相当于新建字符串对象)
    • 需要注意如果拼接的对象用final修饰则不是变量,会被编译优化
  • 如果拼接后的对象主动调用intern方法则将字符串放入堆中字符串常量池,并返回地址
  • 拼接操作与append的效率区别
    • 通过StringBuilder的append方式拼接效率高于直接拼接
    • StringBuilder每次拼接不用反复创建对象
    • 直接拼接每次都要创建对象
    • 创建对象过多影响堆空间和gc



      String的intern方法

      字符串常量池如果存在则直接返回存在的内存地址,都有在字符串常量池新增一个并返回地址

常见问题

  • String str = new String(“123”)创建了几个对象
    • 一个对象是在堆中的str对象
    • 另一个人字符串常量池中的“123”
  • new String(“a”) + new String(“b”)呢
    • 涉及到拼接会先创建一个StringBuilder对象
    • 堆中new String(“a”)
    • 常量池中的a
    • 堆中的new String(“b”)
    • 常量池中的b
    • StringBuilder的toString方法中会再创建一个new String(“ab”)对象,注意此时不会在常量池中创建ab
  • String.intern()
    • 在jdk6中的逻辑是常量池有就返回在常量池中地址没有就在常量池中创建并返回地址
    • jdk7/8中常量池有就返回地址,没有就看堆中对象有没有,如果堆中没有就在字符串常量池创建并返回地址,如果堆中有则在常量池记录堆中对象地址并返回地址



垃圾回收

字符串常量池会被垃圾回收
在G1回收器中对String有去重操作,这里的去重并不是指字符串常量池,其本身就是不会重复的。这里的去重针对的是String对象中记录的value数组,即char/byte[]数组去重。大体思路是对这些数组构建一个hashtable,从而防止重复。




版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接,感谢各位看官!!!

本文出自:monkeyGeek

座右铭:生于忧患,死于安乐

欢迎志同道合的朋友一起交流、探讨!

monkeyGeek
# ,

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×