StringTable常量池与串池的各种联系
一. StringTable常量池与串池的关系
代码:
public class Demo6 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
}
}
反编译后:
"C:\Program Files\Java\jdk-11.0.16.1\bin\javap.exe" -v jvm1.Demo6
Classfile /C:/untitled/out/production/myLife/jvm1/Demo6.class
Last modified 2022年8月23日; size 477 bytes
MD5 checksum ef11e54131b46d597626bc992d2704d3
Compiled from "Demo6.java"
public class jvm1.Demo6
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // jvm1/Demo6
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #6.#24 // java/lang/Object."<init>":()V
#2 = String #25 // a
#3 = String #26 // b
#4 = String #27 // ab
#5 = Class #28 // jvm1/Demo6
#6 = Class #29 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Ljvm1/Demo6;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 s1
#19 = Utf8 Ljava/lang/String;
#20 = Utf8 s2
#21 = Utf8 s3
#22 = Utf8 SourceFile
#23 = Utf8 Demo6.java
#24 = NameAndType #7:#8 // "<init>":()V
#25 = Utf8 a
#26 = Utf8 b
#27 = Utf8 ab
#28 = Utf8 jvm1/Demo6
#29 = Utf8 java/lang/Object
{
public jvm1.Demo6();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Ljvm1/Demo6;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=4, args_size=1
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 args [Ljava/lang/String;
3 7 1 s1 Ljava/lang/String;
6 4 2 s2 Ljava/lang/String;
9 1 3 s3 Ljava/lang/String;
}
SourceFile: "Demo6.java"
Process finished with exit code 0
- 问题分析:
注意:
- StringTable属于Hashtable结构,不能扩容
- Constant pool中的信息,都会被加载到运行时常量池中,这是a b ab都是常量池中的符号,还没有变为java字符串对象
- 例如:
0: ldc #2 会把a符号变为"a"字符串对象(而是用到这行代码才开始创建)
,此时还要准备StringTable串池[],如果第一次找串池中没有本身对象,那么就要放入串池中为[“a”]。但是如果串池中有,那么只调用- 不是每个字符串对象事先放在字符串池里,而是用到这行代码才开始创建(属于懒惰行为)
二. StringTable字符串的拼接
问题分析:
- 问:System.out.println(s3==s4);?
false
原因:s3属于串池中,s4属于堆中,虽然值一样,但是位置不一样
代码:
package jvm1;
public class Demo6 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
}
}
源码分析:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: aload_1
10: aload_2
11: invokedynamic #5, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
16: astore 4
18: return
LineNumberTable:
line 4: 0
line 5: 3
line 6: 6
line 7: 9
line 9: 18
LocalVariableTable:
Start Length Slot Name Signature
0 19 0 args [Ljava/lang/String;
3 16 1 s1 Ljava/lang/String;
6 13 2 s2 Ljava/lang/String;
9 10 3 s3 Ljava/lang/String;
18 1 4 s4 Ljava/lang/String;
}
三. StringTable编译期优化
代码:
package jvm1;
public class Demo6 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s5="a"+"b";
//String s4 = s1 + s2;//new StringBuider().append("a").append("b").toString();
}
}
问题分析:
javac 在编译期间的优化,结果已经在编译期间确定为"ab"
四. StringTable字符串延迟加载
为了查看字符串及其常量池中的变化,这里我采取了IDEA中的调式功能中的Memory(内存问题)子功能
调试1
调试2
调试3
-
说明字符串字面量也是(延迟)成为对象的
五. StringTable特性
常量池中的字符串仅是符号,第一次用到时才变为对象
利用串池的机制,来避免重复创建字符串对象,串池中的字符串有且仅有一个
字符串变量拼接的原理是
StringBuilder (JDK1.8)
字符串常量拼接的原理是编译期优化(编译即确定)
可以使用
intern 方法
,主动将串池中还没有的字符串对象放入串池-
1.8
将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串
池中的对象返回
-
1.6
将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,
放入串池, 会把串池中的对象返回
六. StringTable面试题
package jvm1;
public class Demo5 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b";//ab
String s4 = s1 + s2;//new String("ab")
String s5 = "ab";
String s6 = s4.intern();//由于串池中存入"ab",所以s4还在指向堆中,但是新生成s6是指向串池中的
// 问
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true
String x2 = new String("c") + new String("d");//new String("cd")
String x1 = "cd";//"cd" 此时串池中有["cd"]
x2.intern();//所以x2返回的值不能放入串池中,仍为堆中
System.out.println(x1 == x2);//false
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢 System.out.println(x1 == x2);
System.out.println(x1 == x2);//true
}
}
七. StringTable位置
PerGen 永久代
(JDK1.6)
Metaspace 元空间
(JDK1.8)
- 实例1
- 实例二(解决后)
- 牵扯的代码
package cn.itcast.jvm.t1.stringtable;
import java.util.ArrayList;
import java.util.List;
/**
* 演示 StringTable 位置
* 在jdk8下设置 -Xmx10m -XX:-UseGCOverheadLimit
* 在jdk6下设置 -XX:MaxPermSize=10m
*/
public class Demo1_6 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
int i = 0;
try {
for (int j = 0; j < 260000; j++) {
list.add(String.valueOf(j).intern());
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}
八. StringTable垃圾回收
演示StringTable垃圾回收
- -Xmx10m 设置JVM虚拟机的堆的值
-XX:+PrintStringTableStatistics
打印字符串表的统计信息,可以清楚看到字符串实例的个数和占用的大小信息XX:+PrintGCDetails -verbose:gc
显示垃圾回收的次数时间等
分析1
分析2
分析3
通过分析案例中,我们证明了
StringTable也会发生垃圾回收
- 演示的代码
package cn.itcast.jvm.t1.stringtable;
import java.util.ArrayList;
import java.util.List;
/**
* 演示 StringTable 垃圾回收
* -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
*/
public class Demo1_7 {
public static void main(String[] args) throws InterruptedException {
int i = 0;
try {
for (int j = 0; j < 100000; j++) { // j=100, j=10000
String.valueOf(j).intern();
i++;
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(i);
}
}
}
八. StringTable性能调优
调整 -XX:StringTableSize=桶个数
案例:
分析1
分析2
如果字符串常量个数非常多,那么就可以把-XX:StringTableSize调的非常大,这样就有更好的哈希分布,减少哈希冲突,能够提高性能
- 代码
package jvm1;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/*
演示串池大小对性能的影响
-Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
*/
public class Demo7 {
public static void main(String[] args) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("c:\\a\\sss.txt"), "utf-8"))) {
String line = null;
long start = System.nanoTime();
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
line.intern();
}
System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
}
}
}
考虑将字符串对象是否入池
本文含有隐藏内容,请 开通VIP 后查看