jvmbytecode.class-fileinvokevirtual

How does the JVM know how many values to pop into a new frame when invoking a method via invokevirtual?


When a method is called via invokevirtual, the calling method pops off the values to pass to the called method along with the objectref and places them in the new stack frame.

How does it know which stack entry is the objectref? My guess is that it does so by looking at the type of the called method and parses this to determine how many values to pop off, but this seems extremely inefficient. Is there some other mechanism that I'm overlooking?


Solution

  • When you use the class file format as starting point, the method descriptor is the only way to determine, which values from the operand stack have to become the first local variables of the new stack frame.

    As an exception to the rule, the invokeinterface instruction has an embedded count which can be used to determine the number of (type 1) elements to consume. As the documentation states:

    The count operand of the invokeinterface instruction records a measure of the number of argument values, where an argument value of type long or type double contributes two units to the count value and an argument of any other type contributes one unit. This information can also be derived from the descriptor of the selected method. The redundancy is historical.

    This historical redundancy doesn’t change the fact that the JVM has to cope with method descriptors as the source of this information, e.g. for invokevirtual, invokestatic, invokespecial, or invokedynamic. Further, a conforming JVM is required to verify this information, to throw an error if the invokeinterface’s count differs from the count derived from the method descriptor.

    Generally, the verifier is responsible for detecting when method invocations are inconsistent to the stack frame’s state and therefore, has to process the method descriptors and model their effect on the operand stack. This implies that, unless you’re using a JVM that verifies each instruction right before its actual execution, it has to handle these descriptors even without executing an actual invocation. The obvious solution is to convert the method descriptors into an easier-to-process internal representation in a first step.

    In short, these method descriptors are inefficient but with a reasonable JVM implementation you’re paying the costs only once, not for every invocation.