聊聊jvm中的String
常量池
三种常量池
- Class文件中的常量池:Constant pool中
- 运行时常量池:是在类加载器将Class文件中的常量池解析后存放在方法区中的InstanceKlass类下的ConstantPool* _constants中。
- 字符串常量池:这是堆下的一个数据结构 String Pool,由底层HashTable实现的,一个 key-value的存储形式
- 通过String的内容+长度生成Hash值。将Hash转为key
- Value 是将String类的实例instanceOopDesc 封装成 HashtableEntry。
实验
- String s1 = “11”;
会直接将“11”生成的String对象 封装成HashtableEntry ,并且栈中的变量指向String对象。 会去HashTable里面看看这个key存不存在,那么会直接返回String对象的引用。 - String s1 = new String(“11”) ;
因为调用的new指令那么就会生成一个Stri那个对象,这个时候会去HashTable里面看看这个key存不存在,如果存在直接会返回其String对象的Char数组也就是typeArrayOopDese的引用给新的String对象。
- 在HashTable中寻找的时候如果寻找到了,会直接返回String对象的引用。
- 很明显,在使用new的时候道理一样,不过还是会创造一个String对象实例。
拼接字符串底层是如何实现的
- 双引号 + 双引号
public class TestString_5 {
public static void main(String[] args) {
test2();
}
public static void test2() {
String s1 = "1";
String s2 = "1";
String s = s1 + s2;
}
}
- 字节码
0 ldc #2 <1>
2 astore_0
3 ldc #2 <1>
5 astore_1
6 new #3 <java/lang/StringBuilder>
9 dup
10 invokespecial #4 <java/lang/StringBuilder.<init>>
13 aload_0
14 invokevirtual #5 <java/lang/StringBuilder.append>
17 aload_1
18 invokevirtual #5 <java/lang/StringBuilder.append>
21 invokevirtual #6 <java/lang/StringBuilder.toString>
24 astore_2
25 return
上面可以看得出来,实际上是使用了类似下面的代码
new StringBuilder().append("1").append("1").toString();
// StringBuilder的toString()调用的是:
new String(char[], 0, count);
//上面这种创建会直接生成String实例,并没有加入到常量池中去。
//可以通过intern()方法将其放入常量池。
- 解释下列代码空间的变化
String str ="1"+"2"+new String("3")+"4"+"5"+new String("6")+"7";
//上面看字节码等价于下面
String str ="12"+new String("3")+"45"+new String("6")+"7";
在内存中创建了“12”,”3“,”45“,”6“,”7“的String实例和Char[]实例并且放入了常量池,还有最后的”1234567“的String实例 和 Char[]实例 且没有放入常量池
intern()总结
- JDK1.6中,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址
- JDK1.7起,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址