问题

JVM 常量池中存储的是对象还是引用呢?

回答
JVM 的常量池,无论是类常量池(Class Constant Pool)还是运行时常量池(Runtime Constant Pool),它存储的不是对象本身,而是对各种常量(包括字面量和符号引用)的引用。

为了更清晰地说明这一点,我们不妨想象一下 JVM 在加载类文件时,会经历一个将类文件中描述的各种信息转化为 JVM 内部数据结构的详细过程。

类文件中的常量池:一张“名片簿”

首先,当 JVM 读取一个 `.class` 文件时,类文件格式中有一个专门的区域叫做“常量池”。你可以把这个类文件中的常量池想象成一个“名片簿”,里面记录了类文件中用到的所有字面量(比如字符串、数字)和符号引用(比如类名、方法名、字段名)。

字面量:这些是我们代码中直接写出来的具体值,比如 `"Hello, World!"`、`123`、`3.14f` 等。在类文件常量池中,它们被直接存储为相应类型的值。
符号引用:这些是对类、字段、方法等元素的“名字”的引用。比如,当你代码里有一个 `System.out.println("Hello")` 语句时,`System` 类、`out` 字段、`println` 方法,以及字符串 `"Hello"`,它们在类文件常量池中是以“符号引用”的形式存在的。这些符号引用包含了足够的信息,让 JVM 之后能够找到并定位到这些实际的类、字段或方法。

JVM 如何“使用”这张名片簿

类文件常量池里的内容,只是对这些信息的“预先描述”。JVM 在实际运行的时候,需要将这些“名片”转化为它能够直接操作的“实物”。

当一个类被加载到 JVM 后,JVM 会为这个类创建一个运行时常量池。这个运行时常量池是动态的,它会从类文件的常量池中复制一份内容,并且在这个过程中,一些符号引用会被解析成直接引用。

符号引用到直接引用的转化:想象一下,类文件常量池里的“名片”上写着某个人的名字(符号引用)。当 JVM 需要真正接触这个人时(比如调用一个方法),它需要知道这个人的具体地址(直接引用)。这个地址可能是内存中的一个具体地址,指向了方法在内存中的实际位置。这个解析过程发生在首次使用的时候,或者在类的加载和初始化过程中。

核心区别:存储的是“指向”而不是“实体”

所以,常量池(无论是类文件常量池还是运行时常量池)存储的本质是对这些常量(字面量和符号引用)的引用。它不直接存储一个完整的、可以立即使用的对象实例。

打个比方:

对象:就像一个完整的、可以打电话的手机。
常量池存储的内容:就像一张写着“张三的电话号码是 138xxxx”的名片。这张名片本身不是张三,也不是张三的手机,它只是告诉你如何找到张三的手机(或者说是张三这个人)。

当你代码中需要使用一个字符串常量,比如 `String s = "abc";` 时,JVM 在常量池中找到对字符串 `"abc"` 的引用,然后将这个引用赋值给变量 `s`。如果 JVM 已经创建过字符串 `"abc"` 的实例(通常是通过 String 对象的 intern() 方法或者 JVM 自身的优化),那么 `s` 就会引用那个已经存在的字符串对象;否则,JVM 会创建一个新的 String 对象来表示 `"abc"`,并将其放入字符串常量池(一个特殊的运行时常量池),然后让 `s` 引用这个新创建的对象。

因此,常量池更像是一个索引库或者一个地址簿,它指明了程序中各种常量“所在的位置”或者“如何找到它们”,而不是直接存放那些“被指代”的实体对象。

总结来说,JVM 常量池存储的是各种常量(字面量和符号引用)的引用,这些引用在运行时会被解析为指向实际对象、方法或字段的直接引用。它本身不是对象的集合,而是指向这些对象的“指针”的集合。

网友意见

user avatar

如果您说的确实是runtime constant pool(而不是interned string pool / StringTable之类的其他东西)的话,其中的引用类型常量(例如CONSTANT_String、CONSTANT_Class、CONSTANT_MethodHandle、CONSTANT_MethodType之类)都存的是引用,实际的对象还是存在Java heap上的。

类似的话题

  • 回答
    JVM 的常量池,无论是类常量池(Class Constant Pool)还是运行时常量池(Runtime Constant Pool),它存储的不是对象本身,而是对各种常量(包括字面量和符号引用)的引用。为了更清晰地说明这一点,我们不妨想象一下 JVM 在加载类文件时,会经历一个将类文件中描述的各.............
  • 回答
    JSR 和 RET 指令,以及 returnAddress,是 Java 虚拟机(JVM)在处理一些特定情况下的重要组成部分,尤其是在子程序调用和局部变量表中扮演着关键角色。很多人对它们感到困惑,主要是因为它们的使用场景相对特殊,不像一般的跳转指令那样直观。我们先从 JSR 指令说起。JSR,全称是.............
  • 回答
    好的,我们来聊聊为什么 JVM 不直接用协程(Coroutines)来实现垃圾回收(GC),而是选择其他更传统的方式。这是一个很有意思的问题,涉及到 JVM 设计的权衡、GC 的本质以及协程的适用范围。首先,我们得明确一点:JVM 的 GC 并非一成不变,它是一个不断发展和优化的系统。目前主流的 G.............
  • 回答
    Java 平台中的 JVM (Java Virtual Machine) 和 .NET 平台下的 CLR (Common Language Runtime) 是各自平台的核心组件,负责托管和执行代码。它们都是复杂的软件系统,通常会使用多种编程语言来构建,以充分发挥不同语言的优势。下面将详细介绍 JV.............
  • 回答
    LLVM 对比 JVM 的技术优势,咱们得好好聊聊。要说 LLVM 厉害在哪,那可不是一两句话能说清楚的,它在底层技术上确实有几个过硬的招数,让它在很多场景下都能发挥出比 JVM 更优异的性能和灵活性。首先,最明显的一个优势就是 LLVM 的前后端分离设计。这就像是给它装了个极其灵活的“适配器”。J.............
  • 回答
    Java 之所以诞生了 Java 虚拟机(JVM),很大程度上是它从一开始就被设计成一种“一次编写,到处运行”(Write Once, Run Anywhere)的语言。这个目标是 Java 能够风靡全球的关键,而 JVM 正是实现这一目标的核心技术。在 Java 之前,软件开发往往是针对特定操作系.............
  • 回答
    这个问题很有意思,确实,在很多技术讨论或者技术文章中,我们经常能听到“JVM 调优”,但对“CLR 调优”的说法相对陌生,甚至有些人会觉得 CLR 根本就没有“调优”这个概念。这种感知上的差异,并非空穴来风,背后有着深刻的技术和历史原因。要理解这一点,我们得先回顾一下 Java 和 .NET 的发展.............
  • 回答
    Go 1.6 中关于 GC(垃圾回收)的暂停时间(pause time)以及它与 JVM(Java Virtual Machine)的对比,是一个值得深入探讨的话题。要回答“Go 1.6 中的 GC pause 是否完全超越了 JVM?”这个问题,需要从多个维度进行详细分析,并且要理解“超越”的含义.............
  • 回答
    JavaScript 在 V8 引擎和 Node.js 环境下的开发效率,相比于 Java 在 JVM 和 Vert.x 组合下的确有其独到之处,这主要体现在几个关键的维度上,而不是简单的功能堆砌。首先,JavaScript 的“一次编写,到处运行”的理念在 Web 开发这个根深蒂固的领域带来了巨大.............
  • 回答
    这件事,说实话,刚出来的时候在技术圈里炸开了锅。咱们就掰开了揉碎了,好好聊聊这个“阿里P7程序员、资深架构师不知JVM、红黑树”的争议。事情的起因与“画像”首先,咱们得清楚这个“阿里P7程序员、资深架构师”是怎么被“架”到火上烤的。网上流传的说法,大概是有人在面试或者交流中,发现一些自称是阿里P7或.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有