关于String、常量池的问题
来源:6-12 Java内存模型之常考题解析-2
luyuni
2019-03-02
String s1 = new String(“aaa”);
在内存中的创建过程为:
- 首先在栈内存中为s1分配一块空间。
- 在堆内存中为String对象分配一块空间,并赋初值。
- 执行方法(成员变量赋值、构造代码块共同收敛得到的函数)。
- 调用构造方法
- 讲String对象在内存中的地址赋值给栈中的 s1。
正常对象的创建过程应该是这样的。
我想请问,"aaa"放到了字符串的常量池了吗?它是什么时候被放进常量池的?
我搜索了一下大概的答案都是编译时就确定了,但是编译的时候也没有jvm运行时内存什么事呀。
我的理解:是不是加载的时候时候类加载器将字面量和符号符号引用放入了堆,其它类信息放入方法区,所以"aa"是在类生命周期的加载阶段变放入了常量池,我这样理解对吗?
但是加载在验证的前面呀?没有验证他怎么放进去,怎么放的对。
2回答
-
为解释清楚些,先说三个定义,
首先是字符串常量池,在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个Hash表,默认值大小长度是1009;这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享;
其次是Class常量池,我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量和符号引用;
字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。
第三个是运行时常量池,运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加(String#intern()),符号引用可以被解析为直接引用。
JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。
贴出这么多概念主要是先把编译时和运行时分开,编译的时候会创建出字符串常量池;
具体在第一次引用该项的 ldc 指令被第一次执行到的时候。 解析 CONSTANT_String 时(该代码也就是执行到 Strings = "hello"; 的时候),根据 index 去运行时常量池查找 CONSTANT_UTF8,然后找到对应的 Symbol 对象, 去到 StringTable,StringTable 支持以 Symbol 为 key 来查询是否已经有内容匹配的项存在与否, 存在则直接返回匹配项,不存在则创建出内容匹配的java.lang.String 对象,然后将其引用放入 StringTable。
032019-12-05 -
翔仔
2019-03-03
同学好,这种方式是在构造string的时候放入的字符串常量池
032019-12-06
相似问题