Understanding JVM Constant Loading Instructions

Ondrej Kvasnovsky
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 and sipush 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

--

--

No responses yet