Java中字符串及通用类型/对象的内存存储与组织方式探究
Java字符串对象的内存存储详解
先针对你给出的两段代码逐一分析:
第一段代码:String s1 = "Hello"; String s2 = new String("Hello");
你的理解有部分正确,但存在细节偏差,具体拆解如下:
- 栈(stack)部分:
s1和s2作为局部变量,确实存储在栈中,但它们存储的是对象的内存地址(引用),而非对象本身。 - 字符串常量池:
"Hello"这个字面量在类加载阶段会被存入字符串常量池:- JDK6及更早版本中,常量池属于方法区(对应你说的constants/statics区域);
- JDK7及之后,常量池被迁移到了**堆(heap)**中,这是JVM内存模型的重要调整。
s1的指向:执行String s1 = "Hello";时,JVM会先检查常量池是否存在"Hello",不存在则创建该字符串对象放入常量池,然后将常量池中该对象的地址赋值给s1——所以s1直接指向常量池中的"Hello"对象。s2的指向:执行String s2 = new String("Hello");时,JVM会完成两个操作:- 确保常量池中存在"Hello"(如果之前没有则创建);
- 在堆中创建一个全新的
String对象,这个对象的底层字符数组(JDK8及以前是char[],JDK9+是byte[])会和常量池中的"Hello"共享(因为String是不可变类,共享底层数组不会引发线程安全问题); - 将堆中这个新
String对象的地址赋值给s2。
所以你说的"新创建的包含'Hello'的对象位于heap"是对的,但要注意:堆中的String对象并非复制了常量池的字符串内容,而是共享底层的字符数组。
第二段代码:String s = new String("Hello");
你的认知存在偏差,实际执行流程是:
- 类加载阶段,"Hello"字面量被存入字符串常量池;
- 程序运行到该语句时,JVM在堆中创建一个新的
String对象,这个对象的底层字符数组直接引用常量池中的"Hello"对应的字符数组(而非复制副本); - 最后将堆中这个新
String对象的地址赋值给栈中的s。
关于s1和s2的指向关系
s1直接指向常量池中的"Hello"对象,s2指向堆中的一个独立String对象,但这个堆对象的底层字符数组和常量池中的"Hello"是同一个。因此:
s1 == s2会返回false(因为二者引用的是不同的对象实例);s1.equals(s2)会返回true(因为字符串内容完全一致);- 如果调用
s2.intern(),该方法会返回常量池中"Hello"的引用,此时s2.intern() == s1会返回true。
内容的提问来源于stack exchange,提问作者xcocco
相关产品推荐
相关产品推荐

