StringTable
一.String基本特性
String在JDK9中存储结构变更
根据设置不同的table长度来查看性能变化
二.String的内存分配
三.String的基本操作
四.字符串拼接操作
会进行编译器优化
如果拼接符号的前后出现了变量,则相当于在堆空间中new String() , 具体的内容为拼接的结果
inner() : 判断字符串常量池中是否存在javaEEHadoop的值,如果存在,则返回常量池中javaEEHadoop的地址
如果字符串常量池中不存在字符串,则在字符串中加载一份,并返回此对象的地址
拼接符号左右都是字符串常量或者常量引用
字符串拼接和append的运行效率的差别
通过StringBuilder 的append() 的方式添加字符串的效率要远高于使用String的字符串拼接的方式
好处: StringBuilder的appned() 方式 : 自始至终值创建过一个StringBuilder对象,
使用String的字符串拼接的方式: 创建了多个StringBuilder和String对象
使用String的字符串拼接方式,内存中由于创建了较多的StringBuilder和String对象,内存占用更大,如果进行GC,需要花费额外的时间
public class StringDemo {
private static final int highLevel = 100000;
@Test
public void test1() {
long start = System.currentTimeMillis();
// method1(); // 4656
method2(); // 3
long end = System.currentTimeMillis();
System.out.println("花费的时间是: " + (end - start));
}
public void method1() {
String str = "";
for (int i = 0; i < highLevel; i++) {
str = str + "a"; // 每次循环都会创建一个StringBuilder(),还会创建一个String
}
}
public void method2() {
StringBuilder sb = new StringBuilder();
// 如果预料到需要加的数据比较多,可以再初始化的时候就将创建的对象空间设置的大点,
// 比如100万个数字,可以从空参构造器的93毫秒减少到 64左右
// StringBuilder sb = new StringBuilder(highLevel);
for (int i = 0; i < highLevel; i++) {
sb.append("a");
}
}
}
五.intern()的使用
intern() 的使用 : jdk6 vs jdk7/8
可以查看对应的字节码文件来查看实际执行时创建了几个对象
代码图示
public class String2Demo {
/**
* 注: 在jdk6中,使用intern()方法只是常规的在位于永久代(方法区)中的字符串常量池中创建一个字符串常量
* 而在jdk7/8中,如图代码所示,由于在堆中已经存在了一个new String("11")对象,
* 此时在位于堆中的字符串常常量池中还未有"11"对象,
* 在此时调用intern()方法会在字符串常量池中创建一个"11"对象
* (并不是真的创建了一个"11", 而是创建一个指向new String("11")的对象引用),此时由于节省空间的想法
* 由于堆中已经存在了一个了new String("11") 对象,,,字符串常量池中的"11"对象会指向
* 位于堆中的new String("1"), 即此时字符串常量池中的"11"对象存放的是堆中的new String("11")
* 的地址
* 然后再执行String s4 = "11";的时候,会在字符串常量池中发现已经存在了"11"对象,就会将"11"
* 对象的地址赋给s4,本质上也是指向了堆中的new String("11")
* 所以此时s3 == s4 是true
*
* @param args
*/
public static void main(String[] args) {
String s = new String("1");
s.intern(); // 在调用此方法前,字符串常量池中已经存在了"1"
String s2 = "1";
System.out.println(s == s2); // jdk6 : false jdk7/8 : false
String s3 = new String("1") + new String("1");// s3变量记录的地址为new String("11")
// 执行完上一行代码以后,字符串常量池中,是否存在"11" 呢? 答案 不存在
s3.intern();// 在字符串常量池中生成"11".如何理解 ->
// jdk6 : 创建了一个新的对象"11", 也就有新的地址
// jdk7 : 此时常量中并没有创建"11", 而是创建一个指向堆空间中new String("11")的地址
String s4 = "11";// s4变量记录的地址: 使用是上一行代码执行时,在常量池中生成的"11"的地址
System.out.println(s3 == s4);// jdk6 : false jdk7/8 : true
}
}
示例1:
当加了一行 x = "ab"后
示例2:
public void stringDemo2() {
String s1 = new String("ab");// 执行完之后,会在字符串常量池中生成"ab"
// String s1 = new String("a") + new String("b"); // 执行完之后不会在字符串常量池中生成"ab"
s1.intern(); // 如果上面那句加上这句的话,其实作用就和直接new String("ab") 一样了,这句其实没啥用,因为现在常量池中已经存在了"ab"
String s2 = "ab";// s2 的值是常量池中的"ab"的地址, s1 则是堆中new String("ab")的地址(new String("ab")的ab指向的是常量池中的"ab")
System.out.println(s1 == s2); // false
}
intern() 的效率测试: 空间角度
使用intern()之后,会直接返回常量池中的对象的地址,这样子啊堆中新创建的new String() 就会因为没有引用一段时间后被自动清理掉,一次来达到了节约空间的作用
不用intern()
使用intern()
六.StringTable的垃圾回收
七.G1中的String去重操作