关于String、常量池的问题

来源:6-12 Java内存模型之常考题解析-2

luyuni

2019-03-02

String s1 = new String(“aaa”);

在内存中的创建过程为:

  1. 首先在栈内存中为s1分配一块空间。
  2. 在堆内存中为String对象分配一块空间,并赋初值。
  3. 执行方法(成员变量赋值、构造代码块共同收敛得到的函数)。
  4. 调用构造方法
  5. 讲String对象在内存中的地址赋值给栈中的 s1。

正常对象的创建过程应该是这样的。

我想请问,"aaa"放到了字符串的常量池了吗?它是什么时候被放进常量池的?

我搜索了一下大概的答案都是编译时就确定了,但是编译的时候也没有jvm运行时内存什么事呀。

我的理解:是不是加载的时候时候类加载器将字面量和符号符号引用放入了堆,其它类信息放入方法区,所以"aa"是在类生命周期的加载阶段变放入了常量池,我这样理解对吗?

但是加载在验证的前面呀?没有验证他怎么放进去,怎么放的对。

写回答

2回答

翔仔

2019-03-03

为解释清楚些,先说三个定义,

首先是字符串常量池,在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。

0
3
翔仔
回复
qq_不弃_7
变量的值存在堆中
2019-12-05
共3条回复

翔仔

2019-03-03

同学好,这种方式是在构造string的时候放入的字符串常量池

0
3
翔仔
回复
qq_不弃_7
同学好,不一定呢,jdk8以后,字符串常量池里可以保存实际的值或者指向堆里面字符串的指针。所以有可能是同一份数据
2019-12-06
共3条回复

剑指Java面试-Offer直通车 百度资深面试官授课

招聘季即将到来,让百度资深面试官来为你的高薪Offer保驾护航

8427 学习 · 1870 问题

查看课程