💾 What is the Constant Pool?
The constant pool is a table stored inside every .class
file. It contains a list of symbolic references used by the class — essentially, all the constants and symbolic metadata the class needs.
📜 What Kind of Entries are Stored?
Here’s what you’ll find in a constant pool:
Type of Entry | Examples |
---|---|
Literals | String literals like "Hello World" , numbers like 123 , 3.14 , etc. |
Class References | References to other class names like java/lang/String |
Method References | References to method signatures like println(Ljava/lang/String;)V |
Field References | References to fields like System.out |
Type Information | Types used in methods or fields (I , Ljava/lang/String; , etc.) |
InvokeDynamic Info | For lambdas and dynamic method handles (since Java 7/8) |
📦 Where is the Constant Pool Stored?
1️⃣ In the .class
File
- When you compile your code,
javac
generates a.class
file. - Inside that file, there’s a constant pool table at the very top.
- You can see it using:
javap -verbose MyClass
It looks something like this:
Constant pool:
#1 = Methodref #6.#23 // java/lang/Object."<init>":()V
#2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #26 // Hello World
#4 = Methodref #27.#28 // java/io/PrintStream.println:(Ljava/lang/String;)V
2️⃣ In JVM Memory (Method Area)
- When the class is loaded, this constant pool is copied into the JVM’s Method Area.
- Inside the Method Area, each loaded class has its own runtime constant pool.
🏃♂️ How Does the JVM Access Constant Pool Entries?
This is where the bytecode instructions come in. Most bytecode instructions don’t store hard-coded values — instead, they store an index into the constant pool.
For example, if the bytecode wants to print a string like "Hello World"
, it might look like this:
ldc #3 // load constant #3 from the constant pool (which is "Hello World")
invokevirtual #4 // call println(String), found at entry #4
⚙️ Runtime Process
Here’s how it works step-by-step:
- Bytecode instruction says:
ldc #3
. - The JVM:
- Looks at the constant pool entry #3.
- Sees that entry #3 is a
String
constant:"Hello World"
. - Loads
"Hello World"
into the operand stack.
- The next instruction says:
invokevirtual #4
. - The JVM:
- Looks at the constant pool entry #4.
- Sees it’s a method reference to
PrintStream.println(String)
. - Resolves the actual method pointer.
- Invokes the method.
📊 Constant Pool Example in Action
For this code:
System.out.println("Hello World");
The constant pool might contain:
Index | Type | Value |
---|---|---|
1 | Class | java/lang/System |
2 | Fieldref | System.out |
3 | String | “Hello World” |
4 | Methodref | PrintStream.println(String) |
The bytecode would look something like:
getstatic #2 // Load System.out
ldc #3 // Load constant "Hello World"
invokevirtual #4 // Call println(String)
🚀 Why Use a Constant Pool?
- Compact bytecode: Bytecode only needs small indexes instead of large names.
- Flexibility: Constant pool allows symbolic resolution — actual addresses are resolved at runtime.
- Portability: Symbolic resolution makes it easier to link classes dynamically across different JVM versions.
🔥 Special Case: String Pool
- String literals in the constant pool can also be interned, meaning they are placed into the String Pool (part of the Heap, outside the Method Area).
- This is why
"Hello".intern()
can return the same object across different classes.
✅ Quick Summary
Step | What Happens |
---|---|
Compile | Constant pool is written into .class file |
Class Load | Constant pool is copied into Method Area |
Bytecode Execution | Bytecode instructions access constants via index into constant pool |
Linking | Constant pool entries are resolved to actual classes, methods, and fields when first used |
💡 In Short
✔️ Constant pool is like the symbol table for a class.
✔️ It supports literals, symbolic references, and method/field signatures.
✔️ JVM bytecode accesses data indirectly through the pool, keeping bytecode small and flexible.
✔️ The constant pool is essential for dynamic linking and reflection.