尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Core dump 崩溃排查:JVM 宕机后,那份 core 文件怎么用 gdb 还原现场
Core dump 崩溃排查JVM 宕机后那份 core 文件怎么用 gdb 还原现场凌晨告警进程没了。翻日志最后一行戛然而止没有任何 Java 异常栈——因为它压根不是抛异常死的是崩死的SIGSEGV。arthas attach 不上jstack 打不出JVM 层所有工具在这一刻全部失效。这时候能救你的是那份你**从没确认过到底开没开**的 core 文件。这一篇把 core dump 从怎么开、生成在哪到怎么用 gdb 读崩溃栈一次讲透顺带说清它和hs_err_pid.log的分工以及几个以为开了其实没开的生产大坑。一、core dump 是什么为什么它是 native 崩溃的最后指望core dump核心转储进程运行中收到某些致命信号、异常终止时内核把它当时的内存镜像栈、堆、寄存器、打开的文件等写成一个文件就叫 core 文件。它是一份崩溃瞬间的现场快照事后可以用 gdb 加载它把调用栈、变量、寄存器还原出来。为什么对 Java 进程尤其重要因为 JVM 崩溃分两类类型表现能用什么查Java 层异常抛Exception/Error有完整 Java 栈日志、arthas、jstack 都能抓native / JVM 自身崩溃收到SIGSEGV等信号进程直接死没有 Java 异常栈只剩hs_err_pid.logcore 文件 gdb第二类典型来源JNI 调用的本地库.so越界、JIT 编译器 bug、堆外内存被写坏、JVM 自身缺陷。这时栈顶往往停在一个 native 方法上jstack/arthas 只能看到进了 native 就断了——要穿透到 native 栈core dump gdb 几乎是唯一手段。这正是我们那次战斗服宕机排查里用到的关键一招Java 层看不出所以然最后靠 gdb 分析 core栈顶停在 JNI 调用的 xlua 本地库里才把锅准确甩给了引擎组。那次是怎么用这一篇是怎么开、怎么读的完整方法论那次排查的全过程见《记一次战斗服务器 CPU 打满 100% 且无法恢复的排查》。二、先确认它开着ulimit -c大多数发行版默认是关闭的ulimit -c为 0也就是说——进程崩了不会留下 core 文件事后想查都没得查。所以第一件事是确认并打开。$ulimit-c# 查看当前 core 大小限制0# 0 关闭崩了不生成 core临时打开只对当前 shell 及其子进程生效重连就没了$ulimit-cunlimited# 不限制 core 大小也可写具体值如 ulimit -c 1024000单位 KB为什么常用unlimited早年写 C 程序内存小习惯ulimit -c 1024限制成 1MB 免得 core 太大。但一个 JVM 动辄几个 G 的堆限制太小会导致core 被截断dump 不完整gdb 读出来是残缺的栈反而误导。所以现在一般直接unlimited代价是 core 文件可能很大——这个磁盘问题第三节讲怎么治。持久化打开重启/重连后仍生效常见三种方式/etc/profile或/etc/profile.d/*.sh找到形如ulimit -S -c 0 /dev/null 21的行把0改成unlimitedulimit-S-cunlimited/dev/null21/etc/security/limits.conf走 PAM能按用户/组精细控制* soft core unlimited * hard core unlimited第一列*是所有用户可换成game指定用户或game指定组。注意 soft 和 hard 都要给否则软限制提不上去。两者别打架如果limits.conf里开了/etc/profile里却还留着-c 0登录时后者可能又把它压回 0。改了一处另一处要清掉。⚠️ 最大的坑systemd 拉起的服务上面三种全都不生效。用systemctl start启动的进程不读/etc/profile默认也不走pam_limits所以你在 shell 里ulimit -c unlimited对它毫无影响。必须在 service unit 里配[Service] LimitCOREinfinity改完systemctl daemon-reload systemctl restart your-service再用cat /proc/pid/limits | grep core确认那个具体进程的限制真的变了$cat/proc/12345/limits|grep-icore Max corefilesize unlimited unlimited bytes排查技巧 #1别信我配过了信/proc/pid/limits。core 开没开、堆栈大小、文件句柄数都以目标进程自己的/proc/pid/limits为准而不是你当前 shell 的ulimit -a。游戏服基本都是 systemd/脚本托管这一步最容易翻车。三、core 生成在哪、叫什么名core_pattern开了之后进程崩了 core 落在哪、叫什么由/proc/sys/kernel/core_pattern决定$cat/proc/sys/kernel/core_pattern core# 最朴素的默认就叫 core落在进程的当前工作目录(cwd)core_pattern支持一串占位符来命名常用的占位符含义%p崩溃进程的 PID%e可执行文件名如java注意可能被截断到 15 字符%t崩溃时间Unix 时间戳%s触发崩溃的信号编号如 11 SIGSEGV%h主机名生产环境的推荐做法把 core 集中到一个独立目录、文件名带全信息而不是散落在各进程的 cwdcwd 有时不可写会导致 dump 失败$echo/data/coredump/core-%e-%p-%t-%s/proc/sys/kernel/core_pattern这样崩一个 java 进程会得到类似/data/coredump/core-java-12345-1717401234-11的文件一眼能看出是哪个进程、什么时候、什么信号。要持久化重启不丢写进/etc/sysctl.conf再sysctl -pkernel.core_pattern /data/coredump/core-%e-%p-%t-%s还有个老参数core_uses_pid/proc/sys/kernel/core_uses_pid为 1 时若core_pattern是纯文件名不含%p内核会自动在后面追加.pid。现代内核更推荐直接在core_pattern里用%p这个参数了解即可。systemd 系统上更常见的是管道模式。很多发行版的core_pattern默认是个竖线开头的管道$cat/proc/sys/kernel/core_pattern|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h这表示 core 不落地成裸文件而是交给systemd-coredump统一收集默认压缩存在/var/lib/systemd/coredump/。这时用coredumpctl来查比翻文件方便得多$ coredumpctl list# 列出所有已收集的崩溃TIME PIDUIDGID SIG COREFILE EXE Wed2024-06-0317:01:22 CST123451000100011present /usr/bin/java $ coredumpctl gdb12345# 直接用 gdb 打开这个 core$ coredumpctl dump12345--output/tmp/core-java-12345# 或导出成文件几个会让 core 凭空消失的坑cwd 不可写 / 目录不存在core_pattern指向的目录必须提前建好且进程有写权限否则内核默默 dump 失败什么都不留。磁盘被 core 打满一个 8G 堆的 JVMcore 可能就好几个 G。集中目录要放在有足够空间的独立分区并配好定期清理systemd-coredump有ExternalSizeMax/KeepFree等配置裸目录就自己上cron清。不是所有信号都产生 coreSIGSEGV(11)、SIGABRT(6)、SIGFPE(8)、SIGBUS(7)、SIGILL(4)、SIGQUIT(3) 这类崩溃信号才会 dump而SIGKILL(9) 和SIGTERM(15) 不会。所以kill -9、以及OOM Killer 干掉的进程发的是 SIGKILL根本没有 core——这种要去dmesg//var/log/messages找 OOM 记录而不是傻等 core 文件kill -9与kill -15的区别见《kill -15 和 kill -9 之间差着一份游戏服停机清单》。排查技巧 #2进程消失得干干净净、连 core 都没有时先分清是崩溃SIGSEGV → 有 core / hs_err还是被杀SIGKILL → 无 core查dmesg里的 OOM Killer、或谁执行了kill -9。方向完全不同。四、拿到 core用 gdb 还原崩溃现场前提core 文件 产生它的那个可执行文件对 JVM 就是$JAVA_HOME/bin/java版本必须完全一致否则符号对不上。$ gdb$JAVA_HOME/bin/java /data/coredump/core-java-12345-... GNU gdb(GDB)... Core was generated by java-jarbattle-server.jar. Program terminated with signal SIGSEGV, Segmentation fault.#0 0x00007f8b2c1d3e55 in luaV_execute () from /home/game/server/lib/libbattlecheck.so(gdb)进去之后几条最常用的命令(gdb) bt # backtrace崩溃线程的调用栈最关键的一步 (gdb) info threads # 列出所有线程* 号是当前崩溃线程 (gdb) thread apply all bt # 打印所有线程的栈多线程问题时必看 (gdb) frame 3 # 切到第 3 号栈帧看那一层的上下文 (gdb) info registers # 看寄存器如崩溃地址 (gdb) info sharedlibrary # 看加载了哪些 .so崩在哪个库里 (gdb) quitbt的输出从#0栈顶崩溃点往下读(gdb) bt #0 0x00007f8b2c1d3e55 in luaV_execute () from /home/game/server/lib/libbattlecheck.so #1 0x00007f8b2c1c9a12 in luaD_call () from /home/game/server/lib/libbattlecheck.so #2 0x00007f8b2c1b7f34 in luaD_pcall () from /home/game/server/lib/libbattlecheck.so #3 0x00007f8b2c1a5c21 in battle_check_run () from /home/game/server/lib/libbattlecheck.so #4 0x00007f8b2c194d88 in Java_com_xxx_battle_NativeChecker_check () from /home/game/server/lib/libbattlecheck.so #5 0x00007f8b0402a318 in ?? ()怎么读#0是真正触发 SIGSEGV 的指令所在的函数顺着往下看找到第一个属于你自己/第三方库的帧基本就是责任范围。上例里#0#4全在libbattlecheck.soJNI 本地库内#4那个Java_com_xxx_..._check更是明晃晃的 JNI 入口——崩在本地库执行逻辑里结论清晰可以直接拿着这份栈去找库的维护方。栈底的?? ()是没有符号信息的帧JIT 生成的代码或缺符号通常不影响定位。排查技巧 #3符号越全bt越可读。生产环境的 native 库如果是-O2且 strip 掉符号bt会满屏?? ()。给自己维护的.so保留符号或单独存一份 debug 符号文件、gdb 里bt才有函数名可看。Java 侧的栈则不靠 core——看下一节的hs_err。五、别忘了hs_err_pid.logJVM 崩溃先读它对 JVM 崩溃第一现场往往不是 core而是hs_err_pidpid.log。JVM 自己捕获到致命错误时会在工作目录可用-XX:ErrorFilepath指定生成这份文本报告比裸 core 好读得多# A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc0x00007f8b2c1d3e55, pid12345, tid0x00007f8b1c001700 # # Problematic frame: # C [libbattlecheck.so0x1a3e55] luaV_execute0x315 # # Core dump will be written. Default location: /data/coredump/core-java-12345...往下还有几段极有价值的信息Native frames / Java frames崩溃时既有 native 栈、也有 Java 栈——这是 core gdb 不方便直接给你的gdb 看 native 栈在行看 Java 栈要额外的 SA 工具。hs_err里能直接看到崩溃线程当时在跑哪个 Java 方法。Registers崩溃时寄存器、出错地址。Dynamic libraries加载的所有.so及地址配合pc0x...能算出崩在哪个库的哪个偏移。VM Arguments / EnvironmentJVM 启动参数、内存配置——排查是不是堆外内存/参数问题时直接可用。分工记一句话JVM 崩溃先读hs_err快速定位 Java/native 栈、出问题帧、JVM 参数需要深挖内存内容、逐帧看变量、或hs_err信息不够时再上 core gdb。两者常常是互补的hs_err告诉你崩在libbattlecheck.so的luaV_executecore gdb 让你进一步切到那一帧看它当时在操作什么数据。注意hs_err里那行“Core dump will be written. Default location: …”——它直接告诉你 core 会写在哪。如果这行缺失、或提示不会写 core不同 JVM 版本措辞不一那就是第二节的ulimit/LimitCORE没配好回头补上再复现。六、复盘步骤命令 / 位置目的确认开关ulimit -c/cat /proc/pid/limits崩了到底会不会留 coresystemd 服务unit 里LimitCOREinfinityshell 的 ulimit 对它无效落盘位置cat /proc/sys/kernel/core_patterncore 生成在哪、叫什么systemd 收集coredumpctl list/coredumpctl gdb pid管道模式下用这个查读崩溃栈gdb $JAVA_HOME/bin/java core→bt还原 native 调用栈看全线程thread apply all bt多线程崩溃定位JVM 首选hs_err_pidpid.logJavanative 栈、出错帧、JVM 参数没有 core 时dmesg//var/log/messages是被 OOM Killer /kill -9杀的不产生 core几条可复用的经验平时就把 core dump 开着并验证过别等崩了才发现ulimit -c是 0——那时现场早没了。关键机器ulimit -c unlimitedcore_pattern指向独立目录是标配。systemd 服务要在 unit 里配LimitCORE并以/proc/pid/limits为准这是最常见的以为开了其实没开。JVM 崩溃先hs_err、深挖再 coregdbSIGSEGV类崩溃才有 coreSIGKILL/OOM 没有得去dmesg找。core 会很大、会打满磁盘集中目录 定期清理 保留 native 库符号才能让这份最后的现场真正可用。七、写在最后core dump 是那种平时用不上、用上就是救命的东西——它把进程死亡瞬间的内存冻成一份文件让你在事后还能bt出崩溃的那一刻。对跑着 JNI/本地库的游戏服来说它和hs_err_pid.log一起构成了 Java 工具链失效之后的最后一道排查防线。而这一切的前提是你在还没崩的时候就确认过它开着、知道它会落在哪。
RELATED

相关推荐

ncm转mp3怎么弄?亲测7种实用方法,简单易学

ncm转mp3怎么弄?亲测7种实用方法,简单易学

上周收到一条网友私信,附了张照片——便签纸上歪歪扭扭写着:“下了两百多首歌,全是ncm,车机放不了,咋整?”便签旁边还画了个哭脸。 这个问题我太熟了。网易云音乐下载的歌曲默认存成.ncm格式,属…

📅 2026/10/10 2:24:17
酷狗kgg转mp3怎么弄?7个靠谱方法图文详解

酷狗kgg转mp3怎么弄?7个靠谱方法图文详解

真急人! 昨天下午收到一条网友私信,说他晚上要开车回老家,想把酷狗下载的几十首歌转成mp3放U盘里路上听,结果折腾了一下午一个都没转成功。他随手在便签纸上写了几个问题拍给我——"格式不支持""转换后没声音&quo…

📅 2026/10/10 2:24:17
湖州高端定制浴室柜定制工厂实力公司推荐 本地靠谱服务商

湖州高端定制浴室柜定制工厂实力公司推荐 本地靠谱服务商

高端定制浴室柜怎么选?湖州业主必看的定制工厂科普与避坑指南 在湖州及周边区域的家装过程中,浴室柜作为卫浴空间的颜值担当与收纳核心,越来越受到业主重视。然而市面产品鱼龙混杂,成品柜、贴牌柜、代工柜层层加价、品质参差,如何…

📅 2026/10/10 2:24:17
MORE NEWS

更多资讯

📰

SpringBoot+Vue私人诊所管理系统:协同过滤推荐算法实战解析

这两年我陆陆续续帮几个做基层医疗系统的朋友看过代码,也做过一些私人诊所的信息化改造,发现一个挺有意思的现象:很多诊所老板以为管理系统就是“记个账、排个班”,但真正用了半年之后,最让他们离不开的反而是“推荐”…

📰

MagPie模型路由工具:Agent多模型统一管理与自动分发实践

这个项目叫 MagPie,本质上是一个 Agent 模型路由工具。它的核心思路不是再训练一个多大的模型,而是把市面上已有的各种模型能力统一管起来,根据任务类型自动选择最合适的模型去处理。对于经常在 Agent、工作流、自动化脚本里反复切换模型的人…

📰

用Shell脚本实现轻量级基础设施即代码(IaC)实践

做了这么多年运维,我一直觉得“基础设施即代码”这件事,不应该只有大厂那套玩法。很多小团队、轻量项目,根本不需要立刻上Terraform、Ansible这些重型工具,直接用Shell脚本也能把IaC做得明明白白。这次分享的这套实践,…

📰

本地AI项目部署实战:环境准备、API接口与批量任务全流程解析

高效启动本地 AI 项目:从环境准备到接口联调的一次完整实测打开这篇文章的读者,大概率不是来看概念介绍的,而是想知道三件事:这个项目怎么跑起来、跑起来之后能干什么、遇到问题怎么排查。这次我们就围绕一个本地 AI 工具类项目的…

📰

OpenHarmony真机Flutter应用错误处理与异常管理实战指南

在OpenHarmony真机上跑Flutter,最磨人的不是写页面,而是排错。原因很简单:你在模拟器里跑得好好的逻辑,一旦上了真机,摄像头权限、传感器驱动、系统省电策略、通知开关,任何一环出问题,整个App就…

📰

人类阅读与大语言模型如何应对概念中断和指称中断?

这次我们来看一个研究性项目:Distinct dynamics of conceptual and referential disruptions in human reading and large language model processing,翻译过来是“人类阅读与大语言模型处理中概念与指称中断的不同动态”。它不是一个可以一键部署的模型…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬