Understanding JVM Constant Loading Instructions
3 min readJan 11, 2025
Introduction to Constant Loading
The JVM uses different instructions to load constants onto the operand stack, optimized for different types and sizes of values. Understanding these instructions helps us better comprehend JVM bytecode and optimization techniques.
Types of Load Instructions
1. bipush (Byte Integer Push)
- Used for small integers from -128 to 127
- Takes exactly one byte of storage in bytecode
- Fast and memory-efficient
public class BipushExample {
static void example() {
byte smallNum = 100; // bipush 100
byte negNum = -128; // bipush -128
}
}
Bytecode:
0: bipush 100
2: istore_1
3: bipush -128
5: istore_2
2. sipush (Short Integer Push)
- Used for medium integers from -32768 to 32767
- Takes two bytes of storage
- Still relatively efficient
public class SipushExample {
static void example() {
int mediumNum = 1000; // sipush 1000
int negMedium = -30000; // sipush -30000
}
}
Bytecode:
0: sipush 1000
3: istore_1
4: sipush -30000
7: istore_2
3. ldc (Load Constant)
- Used for larger integers, floats, String references, and Class references
- Loads from constant pool
- More flexible but slightly more overhea
public class LdcExample {
static void example() {
int largeNum = 100000; // ldc 100000
float f = 123.456f; // ldc 123.456
String str = "Hello"; // ldc "Hello"
Class<?> cls = String.class; // ldc class java/lang/String
}
}
4. ldc_w (Load Constant Wide)
- Same as ldc but with wider index into constant pool
- Used when constant pool index doesn’t fit in one byte
- Allows access to larger constant pools
public class LdcWideExample {
// Create enough constants to force ldc_w usage
static final String str1 = "Constant1";
static final String str2 = "Constant2";
// ... (imagine many more constants here)
// ...
static final String str256 = "Constant256";
static void example() {
// This will use ldc_w because the constant's index is high
System.out.println(str256);
}
}
At some point, Constant47
in our case, the compiler will switch from ldc
to ldc_w
:
220: ldc #245 // String Constant44
222: putstatic #247 // Field str44:Ljava/lang/String;
225: ldc #250 // String Constant45
227: putstatic #252 // Field str45:Ljava/lang/String;
230: ldc #255 // String Constant46
232: putstatic #257 // Field str46:Ljava/lang/String;
235: ldc_w #260 // String Constant47
238: putstatic #262 // Field str47:Ljava/lang/String;
241: ldc_w #265 // String Constant48
244: putstatic #267 // Field str48:Ljava/lang/String;
247: ldc_w #270 // String Constant49
250: putstatic #272 // Field str49:Ljava/lang/String;
253: ldc_w #275 // String Constant50
256: putstatic #277 // Field str50:Ljava/lang/String;
259: ldc_w #280 // String Constant51
262: putstatic #282 // Field str51:Ljava/lang/String;
5. ldc2_w (Load Constant 2 Wide)
- Used for double and long constants
- Always uses wide index
- Takes up more space in constant pool
public class Ldc2Example {
static void example() {
long bigNum = 1234567890L; // ldc2_w 1234567890
double d = Math.PI; // ldc2_w 3.141592653589793
}
}
0: ldc2_w #7 // long 1234567890l
3: lstore_0
4: ldc2_w #11 // double 3.141592653589793d
7: dstore_2
Performance Implications
Memory Usage
bipush: 1 byte instruction + 1 byte operand
sipush: 1 byte instruction + 2 bytes operand
ldc: 1 byte instruction + 1 byte index + constant pool entry
ldc_w: 1 byte instruction + 2 bytes index + constant pool entry
ldc2_w: 1 byte instruction + 2 bytes index + constant pool entry (8 bytes for double/long)
Loading Speed
bipush
andsipush
are fastest (immediate values)ldc
family requires constant pool lookup- Difference is usually negligible in modern JVMs
Observing the Constant pool
The constant pool is like a table of contents for your class:
// javap -v output might look like:
Constant pool:
#1 = Integer 100000 // for ldc
#2 = Float 123.456 // for ldc
#3 = String #20 // for ldc
#4 = Long 1234567890 // for ldc2_w
#5 = Double 123.456789 // for ldc2_w
We can view bytecode of the constant pool using
javap -v YourClass
Special cases
There are some special cases that we need to understand when reading the byte-code:
Null Constants
// Special aconst_null instruction
Object obj = null; // aconst_null
Common Integer constants
// Special iconst instructions
int zero = 0; // iconst_0
int one = 1; // iconst_1
int two = 2; // iconst_2
int three = 3; // iconst_3
int four = 4; // iconst_4
int five = 5; // iconst_5
Common Long constants
// Special lconst instructions
long zero = 0L; // lconst_0
long one = 1L; // lconst_1