当前位置:网站首页>美团一面:为什么线程崩溃崩溃不会导致 JVM 崩溃
美团一面:为什么线程崩溃崩溃不会导致 JVM 崩溃
2022-07-03 16:41:00 【InfoQ】

- 线程崩溃,进程一定会崩溃吗
- 进程是如何崩溃的-信号机制简介
- 为什么在 JVM 中线程崩溃不会导致 JVM 进程崩溃
- openJDK 源码解析
线程崩溃,进程一定会崩溃吗?

- 针对只读内存写入数据
- #include <stdio.h> #include <stdlib.h> int main() { char *s = "hello world";// 向只读内存写入数据,崩溃 s[1] = 'H'; }
- 访问了进程没有权限访问的地址空间(比如内核空间)
- #include <stdio.h> #include <stdlib.h> int main() { int *p = (int *)0xC0000fff; // 针对进程的内核空间写入数据,崩溃 *p = 10; }
- 在 32 位虚拟地址空间中,p 指向的是内核空间,显然不具有写入权限,所以上述赋值操作会导致崩溃
- 访问了不存在的内存,比如
- #include <stdio.h> #include <stdlib.h> int main() { int *a = NULL; *a = 1; }
进程是如何崩溃的-信号机制简介

- CPU 执行正常的进程指令
- 调用 kill 系统调用向进程发送信号
- 进程收到操作系统发的信号,CPU 暂停当前程序运行,并将控制权转交给操作系统
- 调用 kill 系统调用向进程发送信号(假设为 11,即 SIGSEGV,一般非法访问内存报的都是这个错误)
- 操作系统根据情况执行相应的信号处理程序(函数),一般执行完信号处理程序逻辑后会让进程退出
// 自定义信号处理函数示例
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
// 自定义信号处理函数,处理自定义逻辑后再调用 exit 退出
void sigHandler(int sig) {
printf("Signal %d catched!\n", sig);
exit(sig);
}
int main(void) {
signal(SIGSEGV, sigHandler);
int *p = (int *)0xC0000fff;
*p = 10; // 针对不属于进程的内核空间写入数据,崩溃
}
// 以上结果输出: Signal 11 catched!
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(void) {
// 忽略信号
signal(SIGSEGV, SIG_IGN);
// 产生一个 SIGSEGV 信号
raise(SIGSEGV);
printf("正常结束");
}
为什么线程崩溃不会导致 JVM 进程崩溃


openJDK 源码解析

JVM_handle_linux_signal(int sig,
siginfo_t* info,
void* ucVoid,
int abort_if_unrecognized) {
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
// 这段代码里会调用 siglongjmp,主要做线程恢复之用
os::ThreadCrashProtection::check_crash_protection(sig, t);
if (info != NULL && uc != NULL && thread != NULL) {
pc = (address) os::Linux::ucontext_get_pc(uc);
// Handle ALL stack overflow variations here
if (sig == SIGSEGV) {
// Si_addr may not be valid due to a bug in the linux-ppc64 kernel (see
// comment below). Use get_stack_bang_address instead of si_addr.
address addr = ((NativeInstruction*)pc)->get_stack_bang_address(uc);
// 判断是否栈溢出了
if (addr < thread->stack_base() &&
addr >= thread->stack_base() - thread->stack_size()) {
if (thread->thread_state() == _thread_in_Java) {
// 针对栈溢出 JVM 的内部处理
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
}
}
}
}
if (sig == SIGSEGV &&
!MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) {
// 此处会做空指针检查
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL);
}
// 如果是栈溢出或者空指针最终会返回 true,不会走最后的 report_and_die,所以 JVM 不会退出
if (stub != NULL) {
// save all thread context in case we need to restore it
if (thread != NULL) thread->set_saved_exception_pc(pc);
uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub;
// 返回 true 代表 JVM 进程不会退出
return true;
}
VMError err(t, sig, pc, info, ucVoid);
// 生成 hs_err_pid_xxx.log 文件并退出
err.report_and_die();
ShouldNotReachHere();
return true; // Mute compiler
}
- 发生 stackoverflow 还有空指针错误,确实都发送了 SIGSEGV,只是虚拟机不选择退出,而是自己内部作了额外的处理,其实是恢复了线程的进程,并抛出 StackoverflowError 和 NPE,这就是为什么 JVM 不会崩溃且我们能捕获这两个错误/异常的原因
- 如果针对 SIGSEGV 等信号,在以上的函数中 JVM 没有做额外的处理,那么最终会走到 report_and_die 这个方法,这个方法主要做的事情是生成 hs_err_pid_xxx.log crash 文件(记录了一些堆栈信息或错误),然后退出
总结
边栏推荐
- 斑馬識別成狗,AI犯錯的原因被斯坦福找到了
- Visual SLAM algorithms: a survey from 2010 to 2016
- Explore Netease's large-scale automated testing solutions see here see here
- 如何在本机搭建SVN服务器
- 【声明】关于检索SogK1997而找到诸多网页爬虫结果这件事
- Aike AI frontier promotion (7.3)
- Record windows10 installation tensorflow-gpu2.4.0
- 【剑指 Offer】58 - I. 翻转单词顺序
- (补)双指针专题
- Multithread 02 thread join
猜你喜欢
Add color to the interface automation test framework and realize the enterprise wechat test report
To resist 7-Zip, list "three sins"? Netizen: "is the third key?"
Two sides of the evening: tell me about the bloom filter and cuckoo filter? Application scenario? I'm confused..
PyTorch 1.12发布,正式支持苹果M1芯片GPU加速,修复众多Bug
What material is sa537cl2 equivalent to in China? Sa537cl2 corresponding material
Yu Wenwen, Hu Xia and other stars take you to play with the party. Pipi app ignites your summer
QT serial port UI design and solution to display Chinese garbled code
NSQ源码安装运行过程
【LeetCode】94. Middle order traversal of binary tree
消息队列消息丢失和消息重复发送的处理策略
随机推荐
在ntpdate同步时间的时候出现“the NTP socket is in use, exiting”
[combinatorics] non descending path problem (outline of non descending path problem | basic model of non descending path problem | non descending path problem expansion model 1 non origin starting poi
Two sides of the evening: tell me about the bloom filter and cuckoo filter? Application scenario? I'm confused..
Mysql 将逗号隔开的属性字段数据由列转行
What material is sa537cl1? Sa537cl1 corresponds to the national standard material
【剑指 Offer】58 - II. 左旋转字符串
PHP二级域名session共享方案
Deep understanding of grouping sets statements in SQL
深入理解 SQL 中的 Grouping Sets 语句
MySQL single table field duplicate data takes the latest SQL statement
IL Runtime
SVN使用规范
Using optimistic lock and pessimistic lock in MySQL to realize distributed lock
斑马识别成狗,AI犯错的原因被斯坦福找到了
CC2530 common registers for port initialization
Mysql 单表字段重复数据取最新一条sql语句
PHP converts a one-dimensional array into a two-dimensional array
There are several APIs of airtest and poco that are easy to use wrong in "super". See if you have encountered them
Pytorch 1.12 was released, officially supporting Apple M1 chip GPU acceleration and repairing many bugs
NSQ source code installation and operation process