在java中,"5/0"语句不会在我的Linux机器上触发SIGFPE信号,为什么?

zha*_*aen 6 c java linux signals exception-handling

我写了一个非常简单的c程序:

#include<stdio.h>

int main(){
    int a=2;
    int b=0;
    printf("%d\n", a/b);
}
Run Code Online (Sandbox Code Playgroud)

并使用strace运行它:strace ./a.out并获得以下输出(仅粘贴尾部)

... ...
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7f04c7fb8000, 4096, PROT_READ) = 0
munmap(0x7f04c7f96000, 127640)          = 0
--- SIGFPE (Floating point exception) @ 0 (0) ---
+++ killed by SIGFPE +++
Floating point exception
Run Code Online (Sandbox Code Playgroud)

输出符合我的预期,因为它被SIGFPE信号杀死.

但是,用Java编写的相同程序,没有得到SIGFPE信号,有没有人知道java进程如何"除以零"异常?

public class Main {

  public static void main(String[] args) {
    int a = 2;
    int b = 0;
    System.out.println(a / b);
  }
}
Run Code Online (Sandbox Code Playgroud)

strace java -Xcomp Main

... ...
mprotect(0xf6949000, 8171520, PROT_READ|PROT_WRITE) = 0
mprotect(0xf6949000, 8171520, PROT_READ|PROT_EXEC) = 0
munmap(0xf774f000, 5727)                = 0
mmap2(NULL, 331776, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xfffffffff68d0000
mprotect(0xf68d0000, 4096, PROT_NONE)   = 0
clone(child_stack=0xf6920494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xf6920bd8, tls=0xf6920bd8, child_tidptr=0xff9c5520) = 958
futex(0xf6920bd8, FUTEX_WAIT, 958, NULL) = 0
exit_group(0)  
Run Code Online (Sandbox Code Playgroud)

nin*_*alj 8

在这里,它提出了一个SIGFPE.

你忘了告诉strace孩子们.添加-f选项strace,你应该看到类似的东西:

[pid  2304] read(3, "\312\376\272\276\0\0\0001\0n\n\0\23\0I\t\0\3\0J\7\0K\n\0L\0M\n\0N\0"..., 2369) = 2369
[pid  2304] --- SIGFPE (Floating point exception) @ 0 (0) ---
[pid  2304] rt_sigreturn(0x1c50800)     = 5
[pid  2304] write(2, "Exception in thread \"main\" ", 27Exception in thread "main" ) = 27
[pid  2304] write(2, "java.lang.ArithmeticException: /"..., 40java.lang.ArithmeticException: / by zero) = 40
[pid  2304] write(2, "\n", 1
Run Code Online (Sandbox Code Playgroud)


MBy*_*ByD 5

与(普通)C编译程序相比,Java程序在运行时运行,而不是在处理器上运行,并且不依赖于平台.在java中除以零会触发ArithmeticException,如下所示:

Exception in thread "main" java.lang.ArithmeticException: / by zero
Run Code Online (Sandbox Code Playgroud)

来自JLS:

抛出异常有三个原因之一:

Java虚拟机同步检测到异常执行条件.出现这种情况是因为:

表达式的评估违反了语言的正常语义,例如整数除以零,如§15.6中所述.

加载或链接程序的一部分时出错(§12.2,§12.3)

超出了对资源的某些限制,例如使用太多内存

  • 对于这个特定的示例代码,如果编译器将其"优化"为"throw new ArithmeticException"语句,我不会感到惊讶.;) (2认同)

pra*_*oid 5

显然这是因为JVM在其代码中有这样的东西:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void fpe_handler(int signum) {
  printf("JVM throws an ArithmeticException here...\n");
  exit (1);
}

int main() {
  int a = 5;
  int b = 0;
  signal(SIGFPE, fpe_handler);
  printf("%d\n", a / b);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

此外,JVM运行多个线程(参见clone()上面的日志或ps -eLf在运行java时执行),因此strace输出只是不完整.

如果更多细节,未处理的SIGFPE表示程序中出现错误.如果java将被SIGFPE杀死,那么它表明JVM中存在错误,而不是在JVM中运行的应用程序中.