在Java中划分两个int
没有什么特别之处。除非处理两种特殊情况之一:
- 除以零。(JVMS要求虚拟机抛出
ArithmeticException
( - 除法溢出(
Integer.MIN_VALUE / -1
,JVMS要求结果等于Integer.MIN_VALUE
((这个问题只针对这种情况(
来自第6章。Java虚拟机指令集。idiv
:
有一种特殊情况不满足这一规则:如果被除数是
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 = %dn", a, b, a / b);
return r;
}
int main() {
divide(INT_MIN, -1);
return 0;
}
我得到的结果(在x86上(:
tmp $ gcc division.c
tmp $ ./a.out
Floating point exception (core dumped)
在ARM(aarch64
(上编译的完全相同的代码产生:
-2147483648 / -1 = -2147483648
因此,在x86上,Hotspot虚拟机似乎需要做额外的工作来处理这种情况。
- 在这种情况下,虚拟机做了什么才能在编译的代码中不损失太多性能
- 它是否利用了POSIX系统中的信号处理可能性?如果是,它在Windows上使用什么
您是对的-HotSpot JVM不能因为特殊情况而盲目使用idiv
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)
但是,正如您可能注意到的,除数==0没有检查。这被认为是一种特殊情况,在正常程序中不应该发生这种情况。这被称为隐式异常。JVM记录可能发生这种异常的地方,并依赖于操作系统信号(或Windows术语中的异常(来处理这种情况。
请参阅os_linux_x86.cpp:
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);
然而,如果隐式异常经常发生在同一位置,JVM会对编译后的代码进行去优化,然后通过显式零检查重新编译(以避免频繁信号处理的性能损失(。
在这种情况下,虚拟机做了什么才能不损失性能编译后的代码太多?
他们什么都不做。它只是作为if语句实现的。
基于目标体系结构有不同的字节码解释器,但我看了看,所有的实现都是一样的。这是x86
inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) {
/* it's possible we could catch this special case implicitly */
if ((juint)op1 == 0x80000000 && op2 == -1) return op1;
else return op1 / op2;
}
我不确定这个评论是在暗示什么。我在JDK邮件列表中找不到任何有趣的关于这个方法的提及,如果我想解释一些历史性的决定,这是我通常会去的。
无论如何,强调"可以"这个词。不管他们的意思是什么,他们都不会这么做。