对比

  • String 是不可变对象
  • StringBuffer是线程安全的,但是有性能损耗
  • StringBuilder是线程不安全的,但是性能更高

  • 为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。

String的创建机制

由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池
其不使用new关键字的String创建机制是:

  • 创建一个字符串时,首先检查池中是否有值相同的字符串对象。
  • 如果有则不需要创建直接从池中刚查找到的对象引用;
  • 如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。

使用new关键字的String创建机制

  • 如果池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”(?)。并返回池中对象的引用
  • 如果池中没有字符串常量“abc”,那么它将首先在池中创建,然后在堆空间中创建(?),并返回堆中对象的引用
  • 因此后者将创建总共 2 个字符串对象。

有争议 别再问我 new 字符串创建了几个对象了!我来证明给你看!
image.png

字符串缓存

  • 把常见应用进行堆转储(Dump Heap),然后分析对象组成,会发现平均 25% 的对象是字符串,并且其中约半数是重复的。如果能避免创建重复字符串,可以有效降低内存消耗和对象创建开销。
  • intern方法,提示JVM把相应的String缓存起来,以备重复使用
  • JDK7后,缓存位置,在堆中。缓存大小,默认是60013
  • Intern是一种显式的排重机制,不太友好=> JDK8后G1 GC的字符串排重
    • 它是通过将相同数据的字符串指向同一份数据来做到的,是 JVM 底层的改变,并不需要 Java 类库做什么修改。
    • -XX:+UseStringDeduplication,并指定G1 GC 。来开启
  • 在运行时,字符串的一些基础操作会直接利用 JVM 内部的 Intrinsic 机制,往往运行的就是特殊优化的本地代码,而根本就不是 Java 代码生成的字节码。

JDK9 对String的改造

在 Java 9 中,引入了 Compact Strings 的设计,对字符串进行了大刀阔斧的改进。将数据存储方式从 char 数组,改变为一个 byte 数组加上一个标识编码的所谓 coder,并且将相关字符串操作类都进行了修改。另外,所有相关的 Intrinsic 之类也都进行了重写,以保证没有任何性能损失。