我们知道以下调用约定:“前六个整数或指针参数在寄存器RDI,RSI,RDX,RCX(Linux内核接口中的R10 [17]: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) {
}
最佳答案
没有指定JVM内部如何调用Java方法。各种JVM实现可能遵循不同的调用约定。这是 Linux x64 上的 HotSpot JVM 的工作方式。
1.解释器方法输入
每个Java方法都有一个进入解释器的入口。此项用于从解释方法跳转到另一解释方法。
rbx
包含一个指向Method*
结构的指针-内部的元数据方法被调用。
r13
保存sender_sp
-调用者方法的堆栈指针。如果使用rsp + 8
适配器,则它可能与c2i
不同(请参见下文)。 HotSpot源代码中的解释器条目的更多详细信息:templateInterpreter_x86_64.cpp。
2.编译后的条目
编译方法具有其自己的入口点。编译代码通过该条目调用编译方法。
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*
参数)。3.适配器
Java方法可能还有两个入口点:
c2i
和i2c
适配器。这些适配器是动态生成的代码,可将编译后的调用约定转换为解释器布局,反之亦然。 с2i
和i2c
入口点分别用于从编译代码中调用解释方法和从解释代码中编译方法。P.S. JVM内部调用方法通常并不重要,因为这些只是最终用户不透明的实现细节。而且,即使在较小的JDK更新中,这些细节也可能会更改。但是,我至少知道一种情况,当Java调用约定的知识显得有用时-分析JVM故障转储时。