Constant Folding in the JVM

Ondrej Kvasnovsky
3 min readJan 12, 2025

--

What is Constant Folding?

Constant folding is a compiler optimization technique where the JVM evaluates constant expressions at compile time rather than computing them at runtime. When the compiler detects that an expression will always yield the same result, it replaces the expression with its computed value.

Simple examples

Basic arithmetic

// Before constant folding
int value = 60 * 24 * 365;

// After constant folding (what actually goes into the bytecode)
int value = 525600; // Computed at compile time

String concatenation

// Before constant folding
String message = "Hello" + " " + "World";

// After constant folding
String message = "Hello World"; // Single constant in bytecode

When does it happen?

Compile-time constants

public class ConstantExample {
// Computed at compile time
private static final int MINUTES_IN_YEAR = 60 * 24 * 365;

// Not computed at compile time (requires runtime information)
private static final int DYNAMIC_VALUE = Math.random() > 0.5 ? 1 : 2;
}

public class ConstantFolding {
// This method's result will be constant folded
static final int computeMaxAge() {
return 60 * 24 * 365; // Will be folded to 525600
}
}

Final fields

public class FinalExample {
// Can be constant folded
private final int CONSTANT = 100 * 100;

// Cannot be constant folded (value known only at construction)
private final int constructorValue;

public FinalExample(int value) {
this.constructorValue = value * 100;
}
}

Complex examples

Mathematical expressions

public class MathExample {
// Will be folded
private static final double CIRCLE_RADIANS = 2 * Math.PI;

// Will NOT be folded (Math.sin is not a constant expression)
private static final double SINE_VALUE = Math.sin(Math.PI / 2);
}

String operations

public class StringExample {
// Will be folded
private static final String GREETING = "Hello".toUpperCase() + "!";

// Will NOT be folded (depends on runtime locale)
private static final String LOCALE_GREETING =
"Hello".toLowerCase(Locale.getDefault());
}

Verifying constant folding

We can use javap to see the bytecode and verify constant folding:

javac YourClass.java
javap -c YourClass

Before we go into exploring byte-code, let’s look at the list of instructions that are being used to load constants: Understanding JVM Constant Loading Instructions.

Example without Constant Folding

public class NoFolding {
public int calculate(int x) {
return x * 60 * 24; // Not folded because x is variable
}
}
no constant folding (bipush is used for small int)

Example with Constant Folding

public class WithFolding {
public int getMinutesInDay() {
return 60 * 24; // Folded to 1440 in bytecode
}
}
constatant folding in action
  • sipush - Pushes a short integer onto the operand stack
  • 1440 - The constant value being pushed (in this case, 60 * 24 which was constant folded!)

Example: static vs static final

class Final {
private static final int A_NUMBER = 2 * 2 * 2;
private static int secondsInDay = 24 * 60 * 60;

public void printValues() {
System.out.println(A_NUMBER); // Will directly use 86400
System.out.println(secondsInDay); // Will load from field
}
}

A_NUMBER constant, it doesn't even appear in the bytecode! This is because it's a static final that's computed at compile time, so its value (86400) is directly embedded wherever the constant is used.

For secondsInDay, we see:

  • The computation (24 * 60 * 60) was constant folded to 86400
  • The value needs to be loaded (ldc) and stored (putstatic) in the field because it's not final
  • It appears in the static initializer block (static {}) because it's a static field

Benefits

Improved Performance

  • No runtime calculation needed
  • Reduced instruction count
  • Better CPU cache utilization (negligible imact)

Reduced Code Size

  • Multiple operations replaced with single constant
  • Smaller bytecode

--

--

No responses yet