我们知道调用约定"前六个整数或指针参数在寄存器RDI,RSI,RDX,RCX(Linux内核接口中的R10:124),R8和R9"中传递给c/c ++代码Linux平台基于以下文章. https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
然而,Linux平台中Java代码的调用约定是什么(假设JVM是热点)?以下是示例,什么寄存器存储这四个参数?
protected void caller( ) { callee(1,"123", 123,1) } protected void callee(int a,String b, Integer c,Object d) { }
apangin.. 10
未指定JVM如何在内部调用Java方法.各种JVM实现可以遵循不同的调用约定.以下是它在Linux x64上的HotSpot JVM中的工作原理.
Java方法可以在解释器中运行,也可以进行JIT编译.
解释和编译的代码使用不同的调用约定.
每个Java方法都有一个进入解释器的入口点.此条目用于从解释方法跳转到另一个解释方法.
所有参数都在堆栈中传递,从下到上.
rbx
包含指向Method*
结构的指针- 被调用方法的内部元数据.
r13
保持sender_sp
- 调用方法的堆栈指针.它可能与rsp + 8
使用c2i
适配器不同(见下文).
有关HotSpot源代码中解释器条目的更多详细信息:templateInterpreter_x86_64.cpp.
编译后的方法有自己的入口点.编译代码通过此条目调用编译的方法.
多达6个第一整数参数被传递在寄存器:rsi
,rdx
,rcx
,r8
,r9
,rdi
.非静态方法接收this
引用作为第一个参数rsi
.
在xmm0
... xmm7
寄存器中最多传递8个浮点参数.
所有其他参数从堆栈上下传递.
在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 | |-------------------------------------------------------|
您可能会注意到Java调用约定看起来类似于C调用约定但右移一个参数.有意这样做是为了避免在调用JNI方法时额外的寄存器重排(你知道,JNI方法JNIEnv*
在方法参数之前有额外的参数).
Java方法可能还有两个入口点:c2i
和i2c
适配器.这些适配器是动态生成的代码片段,它们将编译的调用约定转换为解释器布局,反之亦然.?2i
和i2c
入口点分别用于从已编译的代码和已解释的代码中编译的方法调用解释的方法.
PS JVM内部调用方法通常并不重要,因为这些只是对最终用户不透明的实现细节.此外,即使在较小的JDK更新中,这些细节也可能会发生变化.但是,我知道至少有一种情况,当Java调用约定的知识可能看起来很有用时 - 分析JVM崩溃转储时.
未指定JVM如何在内部调用Java方法.各种JVM实现可以遵循不同的调用约定.以下是它在Linux x64上的HotSpot JVM中的工作原理.
Java方法可以在解释器中运行,也可以进行JIT编译.
解释和编译的代码使用不同的调用约定.
每个Java方法都有一个进入解释器的入口点.此条目用于从解释方法跳转到另一个解释方法.
所有参数都在堆栈中传递,从下到上.
rbx
包含指向Method*
结构的指针- 被调用方法的内部元数据.
r13
保持sender_sp
- 调用方法的堆栈指针.它可能与rsp + 8
使用c2i
适配器不同(见下文).
有关HotSpot源代码中解释器条目的更多详细信息:templateInterpreter_x86_64.cpp.
编译后的方法有自己的入口点.编译代码通过此条目调用编译的方法.
多达6个第一整数参数被传递在寄存器:rsi
,rdx
,rcx
,r8
,r9
,rdi
.非静态方法接收this
引用作为第一个参数rsi
.
在xmm0
... xmm7
寄存器中最多传递8个浮点参数.
所有其他参数从堆栈上下传递.
在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 | |-------------------------------------------------------|
您可能会注意到Java调用约定看起来类似于C调用约定但右移一个参数.有意这样做是为了避免在调用JNI方法时额外的寄存器重排(你知道,JNI方法JNIEnv*
在方法参数之前有额外的参数).
Java方法可能还有两个入口点:c2i
和i2c
适配器.这些适配器是动态生成的代码片段,它们将编译的调用约定转换为解释器布局,反之亦然.?2i
和i2c
入口点分别用于从已编译的代码和已解释的代码中编译的方法调用解释的方法.
PS JVM内部调用方法通常并不重要,因为这些只是对最终用户不透明的实现细节.此外,即使在较小的JDK更新中,这些细节也可能会发生变化.但是,我知道至少有一种情况,当Java调用约定的知识可能看起来很有用时 - 分析JVM崩溃转储时.