It is not specified how JVM calls Java methods internally. Various JVM implementations may follow different calling conventions. Here is how it works in HotSpot JVM on Linux x64.
- A Java method may run in interpreter or it can be JIT-compiled.
- Interpreted and compiled code use different calling conventions.
1. Interpreter method entry
Each Java method has an entry point into the interpreter. This entry is used to jump from an interpreted method to another interpreted method.
- All arguments are passed on stack, from bottom to top.
rbx
contains a pointer to Method*
structure - internal metadata of a
method being called.
r13
holds sender_sp
- stack pointer of a caller method. It may differ from rsp + 8
if c2i
adapter is used (see below).
More details about interpreter entries in HotSpot source code: templateInterpreter_x86_64.cpp.
2. Compiled entry
A compiled method has its own entry point. Compiled code calls compiled methods via this entry.
- Up to 6 first integer arguments are passed in registers:
rsi
, rdx
, rcx
, r8
, r9
, rdi
. Non-static methods receive this
reference as the first argument in rsi
.
- Up to 8 floating point arguments are passed in
xmm0
... xmm7
registers.
- All other arguments are passed on stack from top to bottom.
This convention is nicely illustrated in assembler_x86.hpp:
|-------------------------------------------------------|
| c_rarg0 c_rarg1 c_rarg2 c_rarg3 c_rarg4 c_rarg5 |
|-------------------------------------------------------|
| rcx rdx r8 r9 rdi* rsi* | windows (* not a c_rarg)
| rdi rsi rdx rcx r8 r9 | solaris/linux
|-------------------------------------------------------|
| j_rarg5 j_rarg0 j_rarg1 j_rarg2 j_rarg3 j_rarg4 |
|-------------------------------------------------------|
You may notice that Java calling convention looks similar to C calling convention but shifted by one argument right. This is done intentionally to avoid extra register shuffling when calling JNI methods (you know, JNI methods have extra JNIEnv*
argument prepended to method parameters).
3. Adapters
Java methods may have two more entry points: c2i
and i2c
adapters. These adapters are pieces of dynamically generated code that convert compiled calling convention to interpreter layout and vice versa. с2i
and i2c
entry points are used to call interpreted method from compiled code and compiled method from interpreted code respectively.
P.S. It does not usually matter how JVM calls methods internally, because these are just implementation details opaque to end-user. Moreover, these details may change even in a minor JDK update. However, I know at least one case when the knowledge of Java calling convention may appear useful - when analysing JVM crash dumps.