cac*_*co3 3 java jvm jvm-hotspot
intJava中将两个s相除并没有什么特别之处。除非处理两种特殊情况之一:
ArithmeticException)Integer.MIN_VALUE / -1,JVMS 要求结果等于Integer.MIN_VALUE)(这个问题只针对这种情况)。有一种特殊情况不满足此规则:如果被除数是该
int类型的最大可能数量级的负整数,并且除数为-1,则发生溢出,结果等于被除数。尽管溢出,但在这种情况下不会引发异常。
在我的计算机上 ( x86_64) 本机分区产生SIGFPE错误。
当我编译以下 C 代码时:
#include <limits.h>
#include <stdio.h>
int divide(int a, int b) {
int r = a / b;
printf("%d / %d = %d\n", a, b, a / b);
return r;
}
int main() {
divide(INT_MIN, -1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我得到结果(在 x86 上):
tmp $ gcc division.c
tmp $ ./a.out
Floating point exception (core dumped)
Run Code Online (Sandbox Code Playgroud)
在 ARM ( aarch64)上编译的完全相同的代码产生:
-2147483648 / -1 = -2147483648
Run Code Online (Sandbox Code Playgroud)
因此,在 x86 上,Hotspot VM 似乎需要做额外的工作来处理这种情况。
你是对的 -idiv由于特殊情况,HotSpot JVM 不能盲目使用cpu 指令。
因此 JVM 执行额外的检查,是否Integer.MIN_VALUE除以-1。这种检查存在于解释器和编译代码中。
如果我们用 来检查实际编译的代码-XX:+PrintAssembly,我们会看到类似的东西
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)
Run Code Online (Sandbox Code Playgroud)
然而,正如您可能注意到的,没有检查除数 == 0。这被认为是一种例外情况,在正常程序中永远不会发生。这称为隐式异常。JVM 会记录此类异常可能发生的位置,并依靠操作系统信号(或 Windows 术语中的异常)来处理这种情况。
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);
Run Code Online (Sandbox Code Playgroud)
但是,如果在同一位置发生隐式异常过于频繁,JVM 会取消优化已编译的代码,然后使用显式零检查重新编译它(以避免频繁信号处理的性能损失)。