Java Hotspot JVM如何处理x86上的整数除数溢出?
在Java中划分两个Java Hotspot JVM如何处理x86上的整数除数溢出?,java,jvm,jvm-hotspot,Java,Jvm,Jvm Hotspot,在Java中划分两个ints没有什么特别之处。除非处理两种特殊情况中的一种: 除以零。(JVM要求虚拟机抛出算术异常) 除法溢出(Integer.MIN\u VALUE/-1,JVM要求结果等于Integer.MIN\u VALUE)(此问题专门针对这种情况) 发件人: 有一种特殊情况不满足此规则:如果被除数是int类型的最大可能大小的负整数,且除数是-1,则发生溢出,结果等于被除数。尽管存在溢出,但在这种情况下不会引发异常 在我的计算机上(x86_64)本机分区产生一个SIGFPE错误 当我编
int
s没有什么特别之处。除非处理两种特殊情况中的一种:
算术异常
)Integer.MIN\u VALUE/-1
,JVM要求结果等于Integer.MIN\u VALUE
)(此问题专门针对这种情况)int
类型的最大可能大小的负整数,且除数是-1
,则发生溢出,结果等于被除数。尽管存在溢出,但在这种情况下不会引发异常
在我的计算机上(x86_64
)本机分区产生一个SIGFPE
错误
当我编译以下C代码时:
#包括
#包括
整数除法(整数a,整数b){
int r=a/b;
printf(“%d/%d=%d\n”,a、b、a/b);
返回r;
}
int main(){
除法(整数最小值,-1);
返回0;
}
我得到的结果(在x86上):
tmp$gcc division.c
tmp美元/年输出
浮点异常(内核转储)
在ARM上编译的完全相同的代码(aarch64
)生成:
-2147483648/-1=-2147483648
因此,在x86上,热点虚拟机似乎需要做额外的工作来处理这种情况
- 在这种情况下,虚拟机要做什么才能在编译代码中不损失太多性能
- 它是否利用POSIX系统中信号处理的可能性?如果是,它在Windows上使用什么
无论如何,强调“可以”这个词。无论他们的意思是什么,他们都不会这么做。你说得对-由于特殊情况,HotSpot JVM不能盲目使用
idiv
cpu指令
因此,JVM执行一个额外的检查,Integer.MIN\u值是否除以-1
。此检查同时存在于和中
如果我们用-XX:+printsassembly
检查实际编译的代码,我们将看到如下内容
0x00007f212cc58410: cmp $0x80000000,%eax ; dividend == Integer.MIN_VALUE?
0x00007f212cc58415: jne 0x00007f212cc5841f
0x00007f212cc58417: xor %edx,%edx
0x00007f212cc58419: cmp $0xffffffff,%r11d ; divisor == -1?
0x00007f212cc5841d: je 0x00007f212cc58423
0x00007f212cc5841f: cltd
0x00007f212cc58420: idiv %r11d ; normal case
0x00007f212cc58423: mov %eax,0x70(%rbx)
但是,正如您可能注意到的,没有检查除数==0。这被认为是一种例外情况,在正常程序中永远不会发生这种情况。这称为隐式异常。JVM记录这种异常可能发生的位置,并依赖操作系统信号(或Windows术语中的异常)来处理这种情况
见:
但是,如果隐式异常在同一位置发生得太频繁,JVM会对编译后的代码进行去优化,然后使用显式零检查重新编译(以避免频繁信号处理的性能损失)。您提到的代码永远不会在常规热点构建中执行。这段代码是“C++解释器”的一部分,这是一个用于各种测试和实验的玩具“零汇编”解释器。普通的JDK二进制文件甚至不包含此代码。实际执行的代码是
0x00007f212cc58410: cmp $0x80000000,%eax ; dividend == Integer.MIN_VALUE?
0x00007f212cc58415: jne 0x00007f212cc5841f
0x00007f212cc58417: xor %edx,%edx
0x00007f212cc58419: cmp $0xffffffff,%r11d ; divisor == -1?
0x00007f212cc5841d: je 0x00007f212cc58423
0x00007f212cc5841f: cltd
0x00007f212cc58420: idiv %r11d ; normal case
0x00007f212cc58423: mov %eax,0x70(%rbx)
if (sig == SIGFPE &&
(info->si_code == FPE_INTDIV || info->si_code == FPE_FLTDIV)) {
stub =
SharedRuntime::
continuation_for_implicit_exception(thread,
pc,
SharedRuntime::
IMPLICIT_DIVIDE_BY_ZERO);