对比
- 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 字符串创建了几个对象了!我来证明给你看!
字符串缓存
- 把常见应用进行堆转储(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 之类也都进行了重写,以保证没有任何性能损失。