我们知道 c/的调用约定是“前六个整数或指针参数在寄存器 RDI、RSI、RDX、RCX(Linux 内核接口(interface)中的 R10[17]:124)、R8 和 R9 中传递”基于以下文章的 Linux 平台中的 c++ 代码。 https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
然而,Java 代码在 Linux 平台上的调用约定是什么(假设 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 中的工作方式。
- Java 方法可以在解释器中运行,也可以是 JIT 编译的。
- 解释和编译代码使用不同的调用约定。
1。解释器方法入口
每个 Java 方法都有一个进入解释器的入口点。此条目用于从一个解释方法跳转到另一个解释方法。
- 所有参数都在堆栈上传递,从下到上。
rbx
包含指向Method*
结构的指针 - 一个的内部元数据 正在调用的方法。r13
包含sender_sp
- 调用方方法的堆栈指针。如果使用c2i
适配器,它可能与rsp + 8
不同(见下文)。
有关 HotSpot 源代码中解释器条目的更多详细信息:templateInterpreter_x86_64.cpp .
2。编译入口
编译后的方法有自己的入口点。编译后的代码通过这个条目调用编译后的方法。
- 在寄存器中传递最多 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*
参数)。
3。适配器
Java 方法可能还有两个入口点:c2i
和i2c
适配器。这些适配器是动态生成的代码片段,可将已编译的调用约定转换为解释器布局,反之亦然。 с2i
和i2c
入口点分别用于从编译代码调用解释方法和从解释代码调用编译方法。
P.S. JVM 如何在内部调用方法通常并不重要,因为这些只是对最终用户不透明的实现细节。此外,即使在 JDK 的次要更新中,这些细节也可能发生变化。但是,我知道至少在一种情况下,Java 调用约定的知识可能会显得有用 - 在分析 JVM 故障转储时。
关于java - Java 代码在 Linux 平台上的调用约定是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41693637/