为什么热点生成的编译方法在执行前将 eax 存储在堆栈的高处?
Why do hotspot generated compiled methods stash eax high up the stack before execution?
当查看从 OpenJDK 的 HotSpot 生成的汇编代码时,有一部分序言将 %eax
值复制到堆栈 (rsp-0x14000)
更上层的位置,我不明白为什么它正在这样做:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000120f0f410} 'add' '(JJ)J' in 'Test'
# parm0: rsi:rsi = long
# parm1: rdx:rdx = long
# [sp+0x40] (sp of caller)
0x000000010cd8a3e0: mov %eax,-0x14000(%rsp)
0x000000010cd8a3e7: push %rbp
0x000000010cd8a3e8: sub [=10=]x30,%rsp
0x000000010cd8a3ec: movabs [=10=]x120f0f5d0,%rax
; {metadata(method data for {method} {...} 'add' '(JJ)J' in 'Test')}
这个相同的地址似乎被用于不同编译方法的许多其他分配,我没有看到从哪里读取该内存位置:
# {method} {0x0000000120000090} 'indexOf' '(I)I' in 'java/lang/String'
[Verified Entry Point]
0x000000010c29bfa0: mov DWORD PTR [rsp-0x14000],eax
...
# {method} {0x00000001203fb410} 'add' '(JJ)J' in 'Test'
[Verified Entry Point]
0x000000010c29c3a0: mov DWORD PTR [rsp-0x14000],eax
我是从这段 Java 代码生成的:
public class Test {
public static void main(String args[]) {
long total = 0;
for(int i=0;i<20_000;i++) {
total = add(total,i);
}
System.out.println("Total is: " + total);
}
public static long add(long a, long b) {
return a+b;
}
}
而且我 运行 这个是:
$ java -XX:CompileCommand=print,*.* Test
我使用的 Java 版本是:
$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
它在 OSX 10.11.4
$ uname -a
Darwin alblue.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64
任何人都可以解释这是在做什么,以及它是否会导致写入该地址的争用?我在不同的 JVM 和不同的示例中看到了不同的数字用于偏移量,但是当我看到它被使用时,对于同一会话中 JVM 的所有调用,数字一直保持不变。
叫做stackbanging。我找不到太多关于细节的文档,但粗略的 google 搜索表明它有几个用途,例如通过页面错误处理程序通知 VM 关于堆栈增长的需求,并确保它有足够的余量内部事件,例如方法去优化或在异步异常(例如 SOE 或 OOME)的情况下展开堆栈。
当查看从 OpenJDK 的 HotSpot 生成的汇编代码时,有一部分序言将 %eax
值复制到堆栈 (rsp-0x14000)
更上层的位置,我不明白为什么它正在这样做:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000120f0f410} 'add' '(JJ)J' in 'Test'
# parm0: rsi:rsi = long
# parm1: rdx:rdx = long
# [sp+0x40] (sp of caller)
0x000000010cd8a3e0: mov %eax,-0x14000(%rsp)
0x000000010cd8a3e7: push %rbp
0x000000010cd8a3e8: sub [=10=]x30,%rsp
0x000000010cd8a3ec: movabs [=10=]x120f0f5d0,%rax
; {metadata(method data for {method} {...} 'add' '(JJ)J' in 'Test')}
这个相同的地址似乎被用于不同编译方法的许多其他分配,我没有看到从哪里读取该内存位置:
# {method} {0x0000000120000090} 'indexOf' '(I)I' in 'java/lang/String'
[Verified Entry Point]
0x000000010c29bfa0: mov DWORD PTR [rsp-0x14000],eax
...
# {method} {0x00000001203fb410} 'add' '(JJ)J' in 'Test'
[Verified Entry Point]
0x000000010c29c3a0: mov DWORD PTR [rsp-0x14000],eax
我是从这段 Java 代码生成的:
public class Test {
public static void main(String args[]) {
long total = 0;
for(int i=0;i<20_000;i++) {
total = add(total,i);
}
System.out.println("Total is: " + total);
}
public static long add(long a, long b) {
return a+b;
}
}
而且我 运行 这个是:
$ java -XX:CompileCommand=print,*.* Test
我使用的 Java 版本是:
$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
它在 OSX 10.11.4
$ uname -a
Darwin alblue.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64
任何人都可以解释这是在做什么,以及它是否会导致写入该地址的争用?我在不同的 JVM 和不同的示例中看到了不同的数字用于偏移量,但是当我看到它被使用时,对于同一会话中 JVM 的所有调用,数字一直保持不变。
叫做stackbanging。我找不到太多关于细节的文档,但粗略的 google 搜索表明它有几个用途,例如通过页面错误处理程序通知 VM 关于堆栈增长的需求,并确保它有足够的余量内部事件,例如方法去优化或在异步异常(例如 SOE 或 OOME)的情况下展开堆栈。