STRING
FROM JVM
1、String基本特性
字符串,用一对引号表示("")
实例化的方式
// 1. 这种字面量的赋值方式声明的字符串存在于字符串常量池中, 字符串常量池中不存储相同的字符串
// 如何保证? String Pool底层是一个固定大小的 Hashtable,默认长度60013
// 也可以使用 -XX:StringTableSize设置长度,最小是1009
String s1 = "CUMT";
// 2.
String s2 = new String("CUMT");
String类 默认为final,不能被继承
String类 实现了Serializable接口:字符串是支持序列化的
String类 实现了Comparable接口:String对象可以比较大小
String类 在JDK8及以前是用private final char[] 表示,JDK9以后是用private final byte[]表示,原因:
- String在堆空间中占据了很大部分,有必要对其内部结构进行优化
- 研究发现String大部分存储的都是拉丁字符,也就是说,一个char占据两个字节,但是其中表示的东西一多半没用上,所以改成byte一个字节,为了兼容像中文这类的语言,每一个byte数组还会多一个字符码标示,如果用中文还是用char数组来存
- 基于String类的StringBuffer、StringBuilder也同样做了修改
String实例 代表不可变的字符序列,不可变性,简述一下:
- 对字符串重新赋值需要重写指定内存区域,不可以使用原有的value进行赋值
- 对现有字符串进行连接、替换操作,都需要重新指定内存区域
2、String内存分配
String在内存中放在哪?字符串常量池
-
Java6及以前字符串常量池放在方法区永久代中
-
Java7及以后中所有常量池都保存在堆中
调整原因(Java6 ->)?
- permSize较小,放大量字符串容易使得永久代OOM
- 永久代回收频率低,无法释放内存
3、字符串拼接操作
- 常量和常量的拼接结果在常量池中,原理是编译期优化
- 常量池中不允许存放相同的常量
- 只要其中有一个是变量,结果就在堆(非常量池空间)中,变量拼接原理是``StringBuilder`
- 如果拼接的结果调用
intern(),则主动将常量池中还没有的字符串对象放入池中,返回地址引用
public void test(){
String s1 = "a";
String s2 = "b";
String s3 = "ab";
/*
* new StringBuilder().append("a").append("b");
* toString(); ---> 约等于 new String("ab");
*/
String s4 = s1 + s2;
System.out.pringln(s3 == s4); // false
''''''------------''''''
final String s1 = "a";
final String s2 = "b";
String s3 = "ab";
// final 修饰的算常量
String s4 = s1 + s2;
System.out.println(s3 == s4); //true
}
4、intern()方法
String s = "abc".intern();
String m = "mn";
String q = m.intern();
Java 6中:
判断字符串常量池中是否存在该值,如果存在则返回常量池中地址,如果不存在该值,则在常量池中复制一份儿并返回此字符串的地址
Java7及以后
判断字符串常量池中是否存在该值,如果有则返回常量池中的地址,如果没有,会把对象的引用地址复制一份儿放回串池,并返回串池中保存的引用地址
关于StringBuilder对象的toString
会创建一个String对象,但是不会去往常量池中放入
5、G1中String去重操作
去哪里的重? Heap里面的String对象,因为在字符串常量池中本身就不会有重复的,但是在字符串产量池以外的空间中,存在重复的字符串对象
String s1 = new String("abc");
String s2 = new String("abc");
目的就是让s1、s2指向同一个对象从而减少堆内存
为什么要去重?
实验表明,Java堆中大概有25%的无法被回收的空间被字符串对象占据,同时这其中有一半的字符串存在重复现象,严重消耗了内存,所以我们有必要进行优化
如何去重?
- 当垃圾收集器工作时,会访问堆上存活的对象,对每一个访问的对象都会检查是否是候选的要去重的String对象
- 如果是,就把该对象的引用插入到队列中等待后续的处理,一个去重的线程在后台运行,处理队列中的一个元素意味着从队列中删除这个元素,并尝试去去重它引用的对象
- 用一个hashtable记录所有被String对象使用的不重复的char[],当去重的时候会查这个table,看堆上是否存在一个一模一样的char[]
- 如果存在,String对象会被调整引用哪个数组,释放堆原来数组的引用,最终会被垃圾回收器回收
- 如果不存在,char[]会被插入到hashtable,这样就可以共享这个char[]