You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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会完成两个操作:
    1. 确保常量池中存在"Hello"(如果之前没有则创建);
    2. 在堆中创建一个全新的String对象,这个对象的底层字符数组(JDK8及以前是char[],JDK9+是byte[])会和常量池中的"Hello"共享(因为String是不可变类,共享底层数组不会引发线程安全问题);
    3. 将堆中这个新String对象的地址赋值给s2。

所以你说的"新创建的包含'Hello'的对象位于heap"是对的,但要注意:堆中的String对象并非复制了常量池的字符串内容,而是共享底层的字符数组。

第二段代码:String s = new String("Hello");

你的认知存在偏差,实际执行流程是:

  1. 类加载阶段,"Hello"字面量被存入字符串常量池;
  2. 程序运行到该语句时,JVM在堆中创建一个新的String对象,这个对象的底层字符数组直接引用常量池中的"Hello"对应的字符数组(而非复制副本);
  3. 最后将堆中这个新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 22:01:16