Hello Java developers. Welcome to my new article. Today’s topic is Constant Folding.
What is Constant Folding?
Constant folding is a crucial optimization technique used by the Java Virtual Machine (JVM) and compiler to enhance performance. It is not only utilized in JVM, but also used in other modern languages’ compilers.
This technique eliminates redundant computations by evaluating constant expressions at compile-time rather than runtime. This results in faster execution and reduced bytecode size.
For example, consider the following Java code:
<span>public</span> <span>class</span> <span>ConstantFoldingExample</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span><span>int</span> <span>x</span> <span>=</span> <span>5</span> <span>+</span> <span>10</span><span>;</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>x</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>ConstantFoldingExample</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span> <span>int</span> <span>x</span> <span>=</span> <span>5</span> <span>+</span> <span>10</span><span>;</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>x</span><span>);</span> <span>}</span> <span>}</span>public class ConstantFoldingExample { public static void main(String[] args) { int x = 5 + 10; System.out.println(x); } }
Enter fullscreen mode Exit fullscreen mode
The Java compiler recognizes that 5 + 10
is a constant expression and simplifies it to 15
. As a result, the compiled bytecode does not contain an addition operation but directly loads 15
into the variable.
Let’s explore the bytecode by using javap command line tool. Execute following lines in your terminal.
<span>$ </span>javac ConstantFoldingExample.java<span>$ </span>javap <span>-c</span> ConstantFoldingExample<span>$ </span>javac ConstantFoldingExample.java <span>$ </span>javap <span>-c</span> ConstantFoldingExample$ javac ConstantFoldingExample.java $ javap -c ConstantFoldingExample
Enter fullscreen mode Exit fullscreen mode
The compiled bytecode might look like following:
<span>0</span><span>:</span> <span>bipush</span> <span>15</span> <span>// Push the constant value 15 onto the stack</span><span>2</span><span>:</span> <span>istore_1</span> <span>// Store it in variable x</span><span>3</span><span>:</span> <span>getstatic</span> <span>java</span><span>/</span><span>lang</span><span>/</span><span>System</span><span>/</span><span>out</span> <span>Ljava</span><span>/</span><span>io</span><span>/</span><span>PrintStream</span><span>;</span><span>6</span><span>:</span> <span>iload_1</span> <span>// Load x (which is 15)</span><span>7</span><span>:</span> <span>invokevirtual</span> <span>java</span><span>/</span><span>io</span><span>/</span><span>PrintStream</span><span>.</span><span>println</span> <span>(</span><span>I</span><span>)</span><span>V</span><span>10</span><span>:</span> <span>return</span><span>0</span><span>:</span> <span>bipush</span> <span>15</span> <span>// Push the constant value 15 onto the stack</span> <span>2</span><span>:</span> <span>istore_1</span> <span>// Store it in variable x</span> <span>3</span><span>:</span> <span>getstatic</span> <span>java</span><span>/</span><span>lang</span><span>/</span><span>System</span><span>/</span><span>out</span> <span>Ljava</span><span>/</span><span>io</span><span>/</span><span>PrintStream</span><span>;</span> <span>6</span><span>:</span> <span>iload_1</span> <span>// Load x (which is 15)</span> <span>7</span><span>:</span> <span>invokevirtual</span> <span>java</span><span>/</span><span>io</span><span>/</span><span>PrintStream</span><span>.</span><span>println</span> <span>(</span><span>I</span><span>)</span><span>V</span> <span>10</span><span>:</span> <span>return</span>0: bipush 15 // Push the constant value 15 onto the stack 2: istore_1 // Store it in variable x 3: getstatic java/lang/System/out Ljava/io/PrintStream; 6: iload_1 // Load x (which is 15) 7: invokevirtual java/io/PrintStream.println (I)V 10: return
Enter fullscreen mode Exit fullscreen mode
Does Constant Folding Occur in Only Arithmetic Operations?
The short answer is no. It occurs in following cases.
- Arithmetic Operations (Addition, Subtraction, Multiplication and so on)
- String Concatenation
- Boolean
We’ve already seen how constant folding occurs in arithmetic operations, let’s see other two.
String Concatenation
Consider following Java code
<span>public</span> <span>class</span> <span>ConstantFoldingWithStrings</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span><span>String</span> <span>message</span> <span>=</span> <span>"Hello"</span> <span>+</span> <span>"World"</span><span>;</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Message: "</span> <span>+</span> <span>message</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>ConstantFoldingWithStrings</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span> <span>String</span> <span>message</span> <span>=</span> <span>"Hello"</span> <span>+</span> <span>"World"</span><span>;</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Message: "</span> <span>+</span> <span>message</span><span>);</span> <span>}</span> <span>}</span>public class ConstantFoldingWithStrings { public static void main(String... args) { String message = "Hello" + "World"; System.out.println("Message: " + message); } }
Enter fullscreen mode Exit fullscreen mode
Do the same actions as you did in arithmetic operation in order to get the bytecode.
<span>0</span><span>:</span> <span>ldc</span> <span>#</span><span>7</span> <span>// String HelloWorld</span><span>0</span><span>:</span> <span>ldc</span> <span>#</span><span>7</span> <span>// String HelloWorld</span>0: ldc #7 // String HelloWorld
Enter fullscreen mode Exit fullscreen mode
The output is truncated for simplicity. No need to say anything, it is obvious from the result.
Boolean Folding
Boolean folding might confuse you, but we will see the both possible options ( false
and true
case ).
true
case:
<span>public</span> <span>class</span> <span>ConstantFoldingWithBooleans</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span><span>boolean</span> <span>isBool</span> <span>=</span> <span>3</span> <span><</span> <span>5</span> <span>&&</span> <span>6</span> <span><</span> <span>8</span><span>;</span> <span>// true</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Bool: "</span> <span>+</span> <span>isBool</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>ConstantFoldingWithBooleans</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span> <span>boolean</span> <span>isBool</span> <span>=</span> <span>3</span> <span><</span> <span>5</span> <span>&&</span> <span>6</span> <span><</span> <span>8</span><span>;</span> <span>// true</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Bool: "</span> <span>+</span> <span>isBool</span><span>);</span> <span>}</span> <span>}</span>public class ConstantFoldingWithBooleans { public static void main(String... args) { boolean isBool = 3 < 5 && 6 < 8; // true System.out.println("Bool: " + isBool); } }
Enter fullscreen mode Exit fullscreen mode
Here’s the generated bytecode:
<span>0</span><span>:</span> <span>iconst_1</span><span>1</span><span>:</span> <span>istore_1</span><span>0</span><span>:</span> <span>iconst_1</span> <span>1</span><span>:</span> <span>istore_1</span>0: iconst_1 1: istore_1
Enter fullscreen mode Exit fullscreen mode
iconst_1
opcode means ‘Push int constant 1
‘ and in 1
means true
.
Let’s see the false
case:
<span>public</span> <span>class</span> <span>ConstantFoldingWithBooleans</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span><span>boolean</span> <span>isBool</span> <span>=</span> <span>3</span> <span>></span> <span>5</span> <span>&&</span> <span>6</span> <span><</span> <span>8</span><span>;</span> <span>// false</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Bool: "</span> <span>+</span> <span>isBool</span><span>);</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>ConstantFoldingWithBooleans</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span> <span>boolean</span> <span>isBool</span> <span>=</span> <span>3</span> <span>></span> <span>5</span> <span>&&</span> <span>6</span> <span><</span> <span>8</span><span>;</span> <span>// false</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Bool: "</span> <span>+</span> <span>isBool</span><span>);</span> <span>}</span> <span>}</span>public class ConstantFoldingWithBooleans { public static void main(String... args) { boolean isBool = 3 > 5 && 6 < 8; // false System.out.println("Bool: " + isBool); } }
Enter fullscreen mode Exit fullscreen mode
Generated bytecode:
<span>0</span><span>:</span> <span>iconst_0</span><span>1</span><span>:</span> <span>istore_1</span><span>0</span><span>:</span> <span>iconst_0</span> <span>1</span><span>:</span> <span>istore_1</span>0: iconst_0 1: istore_1
Enter fullscreen mode Exit fullscreen mode
iconst_0
opcode means ‘Push int
constant 0
‘ and in 0
means false
.
Tricky Example
Most of the time, we don’t write code this way by embedding constants directly into expressions. Instead, we store them in variables for better readability and maintainability. However, constant folding will happen when the variables are declared as final
.
Consider following example
<span>public</span> <span>class</span> <span>ConstantFolding</span> <span>{</span><span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span><span>final</span> <span>int</span> <span>campaignPoints</span> <span>=</span> <span>2000</span><span>;</span><span>final</span> <span>int</span> <span>rate</span> <span>=</span> <span>2</span><span>;</span><span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span><span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Amount: "</span> <span>+</span> <span>amount</span><span>);</span><span>}</span><span>public</span> <span>static</span> <span>int</span> <span>getTransactionAmount</span><span>()</span> <span>{</span><span>return</span> <span>3000</span><span>;</span><span>}</span><span>}</span><span>public</span> <span>class</span> <span>ConstantFolding</span> <span>{</span> <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>...</span> <span>args</span><span>)</span> <span>{</span> <span>final</span> <span>int</span> <span>campaignPoints</span> <span>=</span> <span>2000</span><span>;</span> <span>final</span> <span>int</span> <span>rate</span> <span>=</span> <span>2</span><span>;</span> <span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span> <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Amount: "</span> <span>+</span> <span>amount</span><span>);</span> <span>}</span> <span>public</span> <span>static</span> <span>int</span> <span>getTransactionAmount</span><span>()</span> <span>{</span> <span>return</span> <span>3000</span><span>;</span> <span>}</span> <span>}</span>public class ConstantFolding { public static void main(String... args) { final int campaignPoints = 2000; final int rate = 2; int amount = getTransactionAmount() * campaignPoints * rate; System.out.println("Amount: " + amount); } public static int getTransactionAmount() { return 3000; } }
Enter fullscreen mode Exit fullscreen mode
In above example, if we don’t write final keyword in front of the variables, constant folding will not happen at compile time. Let’s see what will be generated as bytecode.
<span>0</span><span>:</span> <span>invokestatic</span> <span>#</span><span>7</span> <span>// Method getTransactionAmount:()I</span><span>3</span><span>:</span> <span>sipush</span> <span>2000</span><span>6</span><span>:</span> <span>imul</span><span>7</span><span>:</span> <span>iconst_2</span><span>8</span><span>:</span> <span>imul</span><span>9</span><span>:</span> <span>istore_1</span><span>0</span><span>:</span> <span>invokestatic</span> <span>#</span><span>7</span> <span>// Method getTransactionAmount:()I</span> <span>3</span><span>:</span> <span>sipush</span> <span>2000</span> <span>6</span><span>:</span> <span>imul</span> <span>7</span><span>:</span> <span>iconst_2</span> <span>8</span><span>:</span> <span>imul</span> <span>9</span><span>:</span> <span>istore_1</span>0: invokestatic #7 // Method getTransactionAmount:()I 3: sipush 2000 6: imul 7: iconst_2 8: imul 9: istore_1
Enter fullscreen mode Exit fullscreen mode
Constant Folding didn’t occur regardless of final
keywords. Why?
The trick is hidden behind the equation.
<span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span><span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span>int amount = getTransactionAmount() * campaignPoints * rate;
Enter fullscreen mode Exit fullscreen mode
Java is calculates the arithmetic operations from left to right. So first operand ( getTransactionAmount()
) is not final
. This is the reason why Constant Folding didn’t occur.
If we wrap the constant variables with parentheses or switch the operands positions, then Constant Folding will occur.
<span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>(</span><span>campaignPoints</span> <span>*</span> <span>rate</span><span>);</span><span>// or</span><span>int</span> <span>amount</span> <span>=</span> <span>campaignPoints</span> <span>*</span> <span>rate</span> <span>*</span> <span>getTransactionAmount</span><span>();</span><span>// or</span><span>final</span> <span>int</span> <span>multiplier</span> <span>=</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span><span>int</span> <span>amount</span> <span>=</span> <span>multiplier</span> <span>*</span> <span>getTransactionAmount</span><span>();</span><span>int</span> <span>amount</span> <span>=</span> <span>getTransactionAmount</span><span>()</span> <span>*</span> <span>(</span><span>campaignPoints</span> <span>*</span> <span>rate</span><span>);</span> <span>// or</span> <span>int</span> <span>amount</span> <span>=</span> <span>campaignPoints</span> <span>*</span> <span>rate</span> <span>*</span> <span>getTransactionAmount</span><span>();</span> <span>// or</span> <span>final</span> <span>int</span> <span>multiplier</span> <span>=</span> <span>campaignPoints</span> <span>*</span> <span>rate</span><span>;</span> <span>int</span> <span>amount</span> <span>=</span> <span>multiplier</span> <span>*</span> <span>getTransactionAmount</span><span>();</span>int amount = getTransactionAmount() * (campaignPoints * rate); // or int amount = campaignPoints * rate * getTransactionAmount(); // or final int multiplier = campaignPoints * rate; int amount = multiplier * getTransactionAmount();
Enter fullscreen mode Exit fullscreen mode
You can choose one of the above. Here’s the generated bytecode
<span>0</span><span>:</span> <span>invokestatic</span> <span>#</span><span>7</span> <span>// Method getTransactionAmount:()I</span><span>3</span><span>:</span> <span>sipush</span> <span>4000</span> <span>// 2000 * 2 = 4000</span><span>6</span><span>:</span> <span>imul</span><span>7</span><span>:</span> <span>istore_1</span><span>0</span><span>:</span> <span>invokestatic</span> <span>#</span><span>7</span> <span>// Method getTransactionAmount:()I</span> <span>3</span><span>:</span> <span>sipush</span> <span>4000</span> <span>// 2000 * 2 = 4000</span> <span>6</span><span>:</span> <span>imul</span> <span>7</span><span>:</span> <span>istore_1</span>0: invokestatic #7 // Method getTransactionAmount:()I 3: sipush 4000 // 2000 * 2 = 4000 6: imul 7: istore_1
Enter fullscreen mode Exit fullscreen mode
This time Constant Folding occurred.
Benefits of Constant Folding
- Improves performance by eliminating redundant computations.
- Reduces bytecode size.
- JVM executes optimized bytecode faster
Conclusion
Constant folding is a powerful optimization that enhances Java program efficiency by precomputing constant expressions.
As always, source code is available on GitHub.
- If you like the article, please consider clapping and following.
- ⭐ Star GitHub repository
- Follow me on LinkedIn | X (Twitter) | GitHub
- Check out my other stories.
暂无评论内容