上篇文章说gc日志以及arthas。
Arthas & GC日志-JVM(十八)
一、常量池
常量池主要放两大类:字面量和符号引用。
字面量就是由字母、数字等构成的字符串或者数值常量。
符号引用主要包含三类常量。
- 类和接口的全限定名。
- 字段的名称和描述符。
- 方法的名称和描述符。
- 字符串常量池
Jkd1.6之前:有永久代,运行时常量池在永久代,运行时常量池里包含字符串常量池。
Jdk1.7:有永久代,但逐步去掉永久代,字符串常量池从运行时常量池分离到堆里。
Jdk1.8之后:无永久代,运行时常量池在元空间,字符串常量池还在堆里。
1、字符串常量池设计思想
因为字符串和对象分配一样,要耗费时间和空间代价较大,作为基础数据,频繁创建字符串对代码性能有影响,为了提高效率,则为字符串开辟字符串缓存区,创建字符串前先在字符串常量池判断是否存在,存在则引用该实例,不存在则放入字符串缓冲区。
- 实际操作
String s = “zhangsan”;
这时候s指向字符串常量池的引用。使用的时候先会去字符串常量池equals比较,一样则直接返回,不一样才会放入字符串缓冲区。
String s1 = new String(“zhangsan”);
S1指向内存对象引用。
这种方法会保证字符串常量池和堆都有这个对象,没有就创建,最后返回堆内存中对象引用。
先去字符串常量池创建字符串对象,再去堆里创建字符串对象“zhangsan”, 最后直接将内存中的引用返回。
String s1=new String(“zhangsan”);
String s2=s1.intern();
System.out.println(s1 == s2);//false
Intern方法是native修饰,先去常量池先找字符串对象,有的话直接返回,没有直接返回指向字符串s1堆里对象的引用。
再看特殊的例子:
String str2 = new StringBuilder(“计算机”).append(“技术”).toString();
System.out.println(str2 == str2.intern())
这个为什么输出的true呢?
在堆内存是有StringBuilder对象,但是会被gc回收,toString则会new String(),这个才是正在返回对象引用。
“计算机技术”没有在常量池中,但在heap堆中,intern则会直接返回堆的引用,所以true。
而“java”属于关键字,早就在常量池,引用对比则是false。