A Java compiler generally follows three rules about the stack when compiling expressions and statements. These rules help guarantee that the code produced will be verifiable.
The steps of evaluating a statement may cause the stack to grow temporarily, but at the end the stack will be empty. Evaluating an expression increases the height of the stack by exactly one, without affecting any of the elements on the stack beneath it.
If a statement uses subexpressions, then each subexpression increases the height of the stack by one, but each is removed before the statement is finished executing. For example, consider the statement
System.out.write(bytes, 0, bytes.length);
where bytes is an array of type byte. This statement contains four subexpressions, System.out, bytes, 0, and bytes.length. In accordance with the third rule, the subexpressions are evaluated first. Suppose that bytes is contained in local variable 1. The subexpressions are translated into
getstatic java/lang/System/out Ljava/io/PrintStream ; System.out aload_1 ; bytes iconst_0 ; 0 aload_1 ; bytes arraylength ; .length
In compiling the expression bytes.length, the code first evaluates the subexpression bytes by loading variable 1 onto the stack. Then it applies the arraylength instruction to it, which pops the result of the subexpression bytes and pushes the value of bytes.length.
The four expressions have increased the height of the stack by four. To finish the statement, all four must be removed from the stack. The next instruction is a method call:
invokevirtual java/io/PrintStream/write ([BII)V
This instruction takes four values off the stack (a PrintStream, an array of bytes, and two ints), returning nothing. Thus, there is no net effect on the stack, even though the stack height was as high as four.
As the compiler compiles the code, it remembers the maximum height the stack can achieve during execution. The class file must specify the maximum allowable height of the stack for each method. This allows the JVM to allocate the smallest possible amount of memory when the method is invoked, improving execution time and memory efficiency. The JVM verification algorithm rejects any programs that attempt to use more stack space than the maximum stack height permits.