001-基础知识专题-关键字和接口-3、String和StringTable
1. String 基本特性
String: 代表不可变的字符序列。简称: 不可变性。
- 当对字符串 重新赋值 时,需要重写指定内存区域赋值,不能使用原有的 value 进行赋值。
- 当对现有的字符串进行 连接操作 时,也需要重新指定内存区域赋值,不能使用原有的 value 进行赋值。
- 当调用 String 的 replace() 方法修改 指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的 value 进行赋值。
- String 实现了 Serializable 接口: 表示字符串是支持序列化的。实现了 Comparable 接口: 表示 String 可以比较大小
- 通过 字面量的方式(区别于 new) 给一个字符串赋值,此时的字符串值 声明在字符串常量池中。
2. 存储结构变更
String 在 jdk8 及以前内部定义了 final char[] value
用于存储字符串数据。jdk9 时改为 final byte[] value
3. StringTable
❕ ^2hufrh
3.1. 特性
3.2. 存放内容
字符串常量
3.3. 位置
3.3.1. 版本演变
总结:
原来 1.6 时,串池放在永久代,之后 fullGC 时才能垃圾回收,增加了字符串内存占用时间,容易导致永久代内存不足,所以从 1.7 之后,把串池放到堆中,miniGC 就可以对串池进行垃圾回收。
3.3.2. 用代码验证 stringtable 位置⭐️🔴
❕ ^ownhou
JDK1.6
JDK1.8
处理办法:
3.3.3. 静态变量本身存放位置
JDK9 之后查看工具:性能调优专题-基础-9、JVM-调优工具
静态变量位置的变化:指的是静态变量的引用,而不是静态变量等号后面的对象,等号后面 new 的对象始终都在堆上。
3.4. 调优⭐️🔴
line 改为 line.intern(),总结:如果项目中存在大量字符串,并且有大量重复值,则可以用入池动作来减少堆内存的消耗
https://www.javatt.com/p/47643
但是如果大量且不重复,就不能使用 intern 功能!否则 YGC 时间会变长
性能调优-进阶-2、系统调优案例♨️3.5. 参考
https://blog.csdn.net/u011635492/article/details/81046174
https://blog.csdn.net/qq_26222859/article/details/73135660
https://www.bilibili.com/video/av70549061?p=36
[[JVM系列-第9章-StringTable(字符串常量池)]]
4. 拼接操作
https://www.bilibili.com/video/BV1PJ411n7xZ?t=351.8&p=122
1.常量与常量的拼接结果在常量池,原理是编译期优化
2.常量池中不会存在相同内容的常量。
3.只要其中有一个是变量,结果就在堆中,相当于 new String()。变量拼接的原理是 StringBuilder ❕ ^1el21s
4.如果拼接的结果调用 intern() 方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
4.1. 编译期优化
“a”、”b” 都是常量,运行期不会再改变,所以编译期直接优化为 “ab”
4.2. 常量拼接
❕ ^rgva19
4.3. append 与 +
JDK1.6 之后,字符串的 + 操作被优化成 StringBuilder 的 append 方法,所以问 String 和 StringBuffer 的区别,在使用的时候,本质是 StringBuilder 和 StringBuffer 的区别,区别就是 StringBuffer 是线程安全的,StringBuilder 是线程不安全的。
1 |
|
很显然,对于任意的一个 demo,当执行次数越大,拼接的效率远低于 StringBuild 的 append(),花费的时间比远超两三个数量级
- 体会执行效率: 通过 StringBuilder 的 append() 的方式添加字符串的效率要远高于使用 string 的字符串拼接方式!
- ① StringBuilder 的 append() 的方式: 自始至终中只创建过一个 StringBuilder 的对象
使用 string 的字符串拼接方式: 创建过多个 StringBuilder 和 String 的对象- ② 使用 String 的字符串拼接方式: 内存中由于创建了较多的 StringBuilder 和 String 的对象,内存占用更大:
改进:
在实际开发中,如果基本确定要前前后后添加的字符串长度不高于某个限定值 highlevel 的情况下建议使用构造器实例化:
stringBuilder s = new StringBuilder(highLevel);//等价于 new char[highLevel]
5. 几个对象⭐️🔴
❕ ^8yadcm
new String("ab")
是创建了 2 个对象,但返回出去的是哪个对象的地址?返回的是堆中对象的地址
1 |
|
❕ ^sc631z
6. intern⭐️🔴
1 |
|
总结 String 的 intern() 的使用:
6.1. 放对象 copy
- jdk1.6 中,将这个字符串对象尝试放入串池。【放 copy 对象】
- 如果串池中有,直接返回已有的串池中的对象的地址
- 如果没有,会从堆中 将对象复制一份,创建一个新对象放入串池,并返回串池中的对象地址
6.2. 放指针
Jdk1.7 起,将这个字符串对象尝试放入串池。【放指针】
- 如果串池中有,直接返回已有的串池中的对象的地址
- 如果没有,则会 拿堆中对象的引用地址复制一份,放入串池,并返回串池中的引用地址
6.3. 总结
6.4. 练习题
1 |
|
7. 3 个池子
性能调优专题-基础-3、JVM-方法区和3个池子8. 实战经验
8.1. 空间查看
-Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails
8.2. 节省空间
8.3. G1 对 String 去重优化
9. 参考与感谢
性能调优专题-基础-4、JVM-堆和GC理论 性能调优专题-基础-4、JVM-堆和GC理论https://blog.csdn.net/wei198621/article/details/112389917
https://codeantenna.com/a/mu1CwajTRu#33_StringBuildappend_186