jvm string 学习笔记

1 String的基本特性

  • string: 字符串,使用一对“”引起来表示

  • String s1 = “ayugudu”;

  • String s2 = new String(“ayugudu”);

  • String 声明为final的,不可被继承

  • String 实现了Serializable接口:表示字符串是支持序列化的。

  • String 实现了Comparable接口:表示string可以比较大小

  • String 在jak8以前内部定义了final char[] value 用于存储字符串数据,jdk9改为byte[]。(不在使用char[]来存储,改成byte[]加上编码标记,节约了空间)

注意:基于string的一些类同样发生了修改如 StringBuffer,StringBuilder


2 String 具有不可变性

  • 当对字符串重新赋值时,需要重写指定内存区域赋值。

  • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

  • 当调用string的replace方法修改指定字符或字符串时也需要重新指定内存区域赋值

  • String的String pool 是一个固定大小的HashTable默认大小是1009(jdk6之前,jdk7默认值就是60013),如果放进String pool 的String非常多,就会造成hash冲突,导致链表很长,调用String.intern性能会下降。

  • 使用-XX:StringTableSize可设置String Table的长度

字符串常量池中是不会存储相同的字符串的

3 string 内存分配

String 类型的常量池比较特殊,主要使用的方法有两种

  • 直接使用双引号声明出来

  • 使用String提供的intern()方法

3.1 StringTable 为什么要调整

jdk1.7 之前将永久代中的字符冲常量池与静态变量存方法到永久代中,之后存放到堆中。

  • permSzie 默认比较小

  • 永久代垃圾回收频率较低

容易出现oom.

4 . 字符串拼接操作

  • 常量与常量的拼接结果在常量池,原理是编译器优化。

  • 常量池中不会存在相同的内容的常量

  • 只要其中有一个是变量,结果就在堆中,变量拼接的原理是stringbuilder

  • 如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

/**
 * @program: jvmDemo
 * @description:
 * @author: wfg
 * @create: 2021-06-19 18:09
 */
public class Test14 {
    public static void main(String[] args) {

        String s1 = "ayu";
        String s2 ="gudu";

        String s3 = "ayugudu";
        //如果拼接的符号前后出现了变量,则相当于在堆空间中 new string(),具体的内容为拼接的结果
        String s4 = s1+s2;
        // 编译期优化
        String s5 ="ayu"+"gudu";
        //true
        System.out.println(s5==s3);
        //false
        System.out.println(s5==s4);

    }
}


String s4 = s1+s2;的执行细节:

  • StringBuilder s = new StringBuilder();

  • s.append(“a”);

  • s.append(“b”);

  • s.toString() —>类似于new String(“ab”)


 final String t1 ="wf";
       final String t2 = "g";
       final String t3 ="wfg";
       String t4 =  t1+t2;
       //true
        System.out.println(t4==t3);

如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非stringbuilder的方式

5 . intern()的使用

如果不是用双引号声明的String 对象,可以使用string提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。

interned string 就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,注意这个值会被存放在字符串内部

6 new String()问题

new String(a)创建了两个对象首先判断常量池中有没有此对象,没有就在常量池中新建此对象并将引用付给堆中新建对象。

String s= new String("a")+new String"b")
```创建了几个对象?

- 对象1: new StringBuilder()

- 对象2new String(“a”)

- 对象3: 常量池中的“a”

- 对象4new String(“b”)

- 对象5 常量池中的“b”

深入剖析 stringBuilder的tostring()

- 对象6new String(“ab”)

**注意,tostring的调用在字符串的常量池中,没有生成ab**

## intern()的问题

```java
/**
 * @program: jvmDemo
 * @description:
 * @author: wfg
 * @create: 2021-05-30 13:19
 */
public class Test4 {

    public static void main(String[] args) {
               String s1= new String("ayugudu");
               s1.intern();//调用此方法之前,字符串常量池中已经存在"ayugudu"
               String s2="ayugudu";
               System.out.println(s1==s2);// jdk6:false  jdk7:false



               String t1 = new String("wf")+new String("g");//t1记录变量的地址为 new String("wfg"),执行完后字符串常量池中没有wfg

               t1.intern();//在字符串常量池中生成“11” 理解:
                                                // jdk6:创建了一个新的对象“wfg”,拥有新的地址
                                               //  jdk7:此时常量中没有创建“wfg”,而是创建一个指向堆空间中 new String(“wfg”)的地址
               String t2 ="wfg";// t2 所使用的是 上一行代码在字符串常量池中生成的地址
               System.out.println(t1==t2);// jdk6:false     jdk7:true
    }

}

总结:

  • jdk1.6中,将这个字符串对象尝试放入常量池中

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

    • 如果串池中有,则不会放入

    • 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。

对于程序中大量存在的字符串,尤其是其中存在很多重复字符串时,使用intern()可以节省内存空间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值