Day337&338.StringTable -JVM
Posted 阿昌喜欢吃黄桃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day337&338.StringTable -JVM相关的知识,希望对你有一定的参考价值。
StringTable
一、String基本特性
字符串常量池不放重复字符串
1、基本特性
2、String在jdk9中存储结构变更
为什么在jdk9中将String的底层value存放的形式变为byte[]字节数组
3、String存储结构变更
二、String内存分配
- 存放流程:↓
- 图示:↓
三、String基本操作
常量池不放重复字符串
- 下面代码执行流程
左侧栈帧,右侧堆空间
四、字符串拼接操作
1、常量与常量拼接结构在常量池;通过编译器优化
2、只要其中有一个是变量,结果就在堆中;通过StringBuilder
3、如果声明的变量被final修饰
如果变量被final修饰
,就会直接去引用字符串常量池里面的常量;
字符串拼接操作不一定使用的都是StringBuilder!
如果拼接符号左右两边
都是字符串常量(String a = "XXX";)
或常量引用(被final修饰)
,仍然使用编译器优化
针对于final修饰类、方法、基本数据类型、引用数据类型的量结构时,能使用上final的时候建议就使用上。
4、拼接操作与使用append()效率对比
使用字符串拼接意味着,每次循环都要创建一次 StringBuilder、String对象
五、intern()的使用
1、API介绍
2、如何保证变量s指向的是字符串常量池中的数据?
3、面试题
4、JDK6 VS JDK 78的intern()总结
new String(“a”)就是在堆空间里创建一个空间和在字符串常量池中创建一个a,把堆空间中的引用给s变量;
s.intern()会判断s变量的a,是否在字符串常量池存在,这里的情况是已经存在了,所以没做什么;他也没有用变量接收;
因此,String s2 = “a”;这里变量s2的引用就会去直接引用在字符串常量池中已存在的a
因此比较变量s1和s2的地址值,必然是不一样的,s指向的是堆空间的a,而s2指向的是字符串常量池中的a,所以返回false
JDK6:↓↓
里面s3的操作这里,有创建4个对象:①StringBuilder②堆空间a③字符串常量池a④堆空间StringBuilder的append()结束后的new 的String(“aa”)对象,但他不会创建在字符串常量池中的aa对象;
此时,字符串常量池中有a,但没有aa,堆空间有StringBuilder、a、aa;
s3就相当于new了个aa,在堆中创建aa,但没有在字符串常量池中创建aa;
下面又执行了s3.intern(),就是判断字符串常量池中是否有aa,这里的情况是没有,所以就创建aa在字符串常量池;
接下来的s4就显式的指定了"aa",那肯定是字符串常量池中的aa的地址;
所以最后判断s3 与s4的地址 是false,一个是堆空间aa,一个是字符串常量池的aa
在JDK6,a.intern()会判断字符串常量池中是否有,如果没有就把a对象
堆空间的内容
复制一份,并放入字符串常量池
JDK7/8:↓↓
在JDK7/8中其他的一开始的部分是一样的,也是在给s3赋给堆空间aa,但没有创建字符串常量池的aa;
但接下来执行的s3.intern()方法,他在判断,判断字符串常量池中是否有aa,这里的情况肯定是没有的;
但是!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
他这里有做了改变,他确实在字符串常量池中创建了aa对象,但是!!但是!!;
他这里字符串常量池里aa对象的引用地址就直接引用了堆空间的aa的地址,
为了节省空间
所以接下来s4 = “aa”,他给他赋上字符串常量池的aa地址,但是字符串常量池中的aa地址又指向了堆空间aa的地址,所以最终 s3的地址是堆空间aa的地址,s4的地址是字符串常量池aa的地址,但是字符串常量池aa的地址是堆空间aa的地址;
所以 s3 == s4;true
在JDK7/8,a.intern()会判断字符串常量池中是否有,如果没有就把a对象的
堆空间的引用复
制一份,并放入字符串常量池
- 总结:
- 练习1
JDK6:↓
String s = new String(“a”)+new String(“b”);相当于就是String s = new String(“ab”)但是他没有在字符串常量池里面生成ab,当在堆空间创建了ab;
那么String s2 = s.intern(); 就会在判断在字符串常量池中判断是否有ab了没,结果是没有,所以s2就是赋值堆空间的内容,在字符串常量池创建ab的地址;
所以s2的地址是字符串常量池ab的地址,所以 s2 == “ab”,为true;
s ==“ab”,这里的"ab"是字符串常量池的ab,但是s的堆空间ab的地址,所以为false;
JDK78:↓
这里在JDK78的情况如上类似;
String s = new String(“a”)+new String(“b”);相当于就是String s = new String(“ab”)但是他没有在字符串常量池里面生成ab,当在堆空间创建了ab;
那么String s2 = s.intern(); 就会在判断在字符串常量池中判断是否有ab了没,结果是没有,所以s2就是赋值堆空间ab的引用,赋值给字符串常量池ab的地址;
所以s2的地址就是字符串常量池ab的地址,字符串常量池ab的地址是堆空间ab的地址;
所以s2 ==“ab”,s2是字符串常量池的地址,他s2引用的地址是字符串常量池的地址,字符串常量池的地址引用的堆空间ab的地址;为true;
而s ==“ab”,s是堆空间ab的地址,"ab"是字符串常量池的地址,但是!!"ab"字符串常量池的地址引用的是堆空间"ab"的地址,所以 为true;
- 练习2:↓
String x = “ab”;会在字符串常量池中创建"ab";
String s = new String(“a”)+new String(“b”);等于 String s = new String(“ab”),但是没有在字符串常量池中创建ab,而在堆空间创建ab,所以s是堆空间ab的地址;
String s2 = s.intern(),会先判断字符串常量池中是否有ab,显然我们之前就已经在字符串常量池中创建了ab,所以s2的地址是字符串常量池ab;
s2 == x,s2是字符串常量池ab的地址,x为字符串常量池ab的地址,所以为true;
s = x,s是堆空间ab的地址,x为字符串字符串常量池ab的地址,所以为true;
这里不存在jdk6与78的区别,因为一开始x =ab;这里就已经在字符串常量池中放入了ab,就没有区别了
5、intern()效率测试:空间角度
在操作后是否调用intern(),来影响空间问题
六、StringTable的垃圾回收
七、G1中的String去重操作
字符串常量池里面本身不可能有重复的数据
1、背景
2、实现
3、命令行选项
以上是关于Day337&338.StringTable -JVM的主要内容,如果未能解决你的问题,请参考以下文章
<LeetCode OJ> 337. House Robber III