一:故障现象
Java微服务进程崩溃,发现Linux系统日志messages中有killed by SIGABRT的日志
日志位置
#cat /var/log/messages | grep "killed"
Jul 12 09:33:54 prod4-app8 abrt-hook-ccpp: Process 6245 (java) of user 1002 killed by SIGABRT - dumping core
二:messages日志说明
Linux 系统中的 /var/log/messages
日志文件是一个核心的系统级日志文件,它记录了内核、系统服务、守护进程以及其他关键系统组件产生的广泛信息。这些信息对于系统管理员进行故障排除、监控系统运行状况和审计安全事件至关重要。
以下是 /var/log/messages
通常记录的主要内容:
-
内核消息:
-
系统启动和关闭过程中的关键信息。
-
硬件检测和初始化信息(如 CPU、内存、磁盘、USB 设备、网卡等)。
-
硬件错误和警告(如磁盘 I/O 错误、内存问题、温度警报)。
-
内核模块加载和卸载 (
insmod
,rmmod
,modprobe
)。 -
内核参数更改。
-
与虚拟化相关的事件(如果使用了 KVM、Xen 等)。
-
-
系统服务/守护进程的启动、停止和状态消息:
-
当系统进入不同的运行级别时,启动或停止的服务。
-
sshd
,crond
,syslogd
/rsyslogd
/journald
,httpd
/nginx
,mysqld
/postgresql
,dnsmasq
/bind
等常见服务的启动、停止、重启和运行状态信息。 -
服务初始化过程中的错误或配置问题。
-
-
认证和安全相关事件:
-
用户登录(通过控制台、SSH 等)和注销记录(通常更详细的信息在
/var/log/secure
或/var/log/auth.log
,但关键认证事件有时也会出现在messages
)。 -
sudo
命令的执行(同样,主要记录在secure
/auth.log
,但关键失败或配置问题可能出现在messages
)。 -
PAM 认证模块的消息。
-
与
cron
作业执行相关的身份验证(有时)。
-
-
磁盘和文件系统事件:
-
文件系统挂载和卸载。
-
文件系统检查 (
fsck
) 的结果。 -
磁盘空间警告(通常由
cron
任务如logwatch
或sysstat
生成)。 -
与 LVM 或 RAID 配置相关的重要事件。
-
-
网络配置事件:
-
网络接口启动、关闭、状态变化。
-
DHCP 客户端获取或更新 IP 地址的信息。
-
重要的防火墙规则变更(如
iptables
/nftables
服务重启)。
-
-
系统错误和警告:
-
应用程序或服务崩溃的核心转储通知(如果配置了)。
-
资源耗尽警告(如内存不足 OOM killer 被触发)。
-
其他未能归类到更专业日志文件(如
cron
,secure
,maillog
)的系统级错误和警告。
-
-
定时任务 (
cron
) 执行摘要:-
cron
守护进程本身的活动(启动、关闭)。 -
用户
cron
作业执行时产生的输出(如果作业没有将输出重定向到其他地方,默认会发送邮件给用户,系统cron
作业的输出通常也会记录在这里或/var/log/cron
)。
-
重要说明:
-
发行版差异: 随着 Linux 日志系统的发展(从
syslog
到rsyslog
再到systemd-journald
),不同发行版对/var/log/messages
的使用有所变化。-
传统 Syslog 系统 (RHEL/CentOS 7 及更早, 一些较旧的发行版):
/var/log/messages
是主要的、综合的系统日志文件,包含上述大部分内容。 -
Rsyslog 系统 (现代 RHEL/CentOS, Fedora, openSUSE 等):
/var/log/messages
通常仍然是核心日志文件,但配置更灵活,内容可能根据/etc/rsyslog.conf
的设置而有所不同。其他更专业的日志文件(如secure
,cron
,maillog
)通常会将更具体的消息分流出去。 -
Systemd Journald 系统 (现代 Debian/Ubuntu, Fedora, RHEL/CentOS 8+): 默认情况下,
/var/log/messages
可能不存在或不作为主要日志。journald
将日志存储在二进制格式的journal
中(通常位于/var/log/journal/
)。不过,这些系统通常配置rsyslog
或syslog-ng
作为补充,并将journal
的消息转发到传统的文本日志文件。在 Ubuntu 和 Debian 上,最常见的综合系统日志文件通常是/var/log/syslog
。/var/log/messages
在 Ubuntu 上通常不存在或为空,除非特别配置。
-
-
日志轮转: 日志文件会定期轮转(例如通过
logrotate
工具),旧的日志会被压缩或删除。你可能会看到messages
,messages.1
,messages.2.gz
等文件。 -
查看日志: 查看
messages
日志通常使用命令行工具:-
tail -f /var/log/messages
(实时查看最新追加的内容) -
less /var/log/messages
(分页查看整个文件) -
grep "error" /var/log/messages
(搜索包含特定关键词的行) -
journalctl
(在主要使用journald
的系统上查看日志)
-
总结: /var/log/messages
(或其等效文件如 Ubuntu 的 /var/log/syslog
) 是 Linux 系统的“总控台”,它记录了内核、核心系统服务以及各种关键系统事件的广泛信息,是系统管理员进行监控、调试和维护的首要信息来源。务必注意不同发行版在默认日志文件路径和机制上的差异。
三:查看Java的报错日志hs_err_pid
messages记录的信息太少了,很难知道具体的崩溃原因,需要查看Java的hs_err_pid日志
先看看hs_err_pid的说明:
Java 程序崩溃(特别是 JVM 自身崩溃)时,通常会生成名为 hs_err_pid<pid>.log
的日志文件(例如 hs_err_pid12345.log
)。这个文件是 HotSpot 虚拟机(大多数 Java 发行版使用的 JVM)在遇到无法恢复的致命错误(Fatal Error)时自动生成的错误报告文件,是诊断 JVM 崩溃问题的首要资源。
主要记录了以下关键信息:
-
崩溃原因概要:
-
文件头部会明确指出导致 JVM 崩溃的根本原因(通常以
# A fatal error has been detected by the Java Runtime Environment:
开头)。 -
常见原因包括:
-
EXCEPTION_ACCESS_VIOLATION
: 试图访问无效内存地址(最常见,通常是本地代码问题或 JVM bug)。 -
EXCEPTION_STACK_OVERFLOW
: 栈溢出(可能是无限递归或本地代码栈问题)。 -
SIGSEGV
: 段错误信号(非法内存访问)。 -
SIGBUS
: 总线错误(内存对齐问题等)。 -
SIGFPE
: 算术错误(如除以零)。 -
Internal Error
: JVM 内部的严重错误。 -
OutOfMemoryError
: 当无法分配更多本地内存(Native Memory)时(注意:Java Heap 的OutOfMemoryError
是 Java 异常,通常不会导致 JVM 崩溃并生成 hs_err 文件;但本地内存耗尽会导致 JVM 崩溃)。
-
-
-
问题发生的时刻和进程信息:
-
时间戳。
-
JVM 进程 ID (
pid
). -
可执行文件路径 (
jre/bin/java
)。
-
-
系统信息:
-
操作系统名称、版本、内核版本。
-
系统架构(x86, x86_64, arm 等)。
-
主机名。
-
CPU 信息(型号、核心数、特征)。
-
-
JVM 版本和配置信息:
-
Java 运行时版本(
java -version
的输出)。 -
Java 虚拟机信息(VM 版本、模式 Client/Server)。
-
完整的 JVM 启动命令行参数 (
CommandLine flags
)。这是极其重要的信息,包含了所有-X
,-XX
参数。 -
类路径 (
CLASSPATH
)。 -
库路径 (
LD_LIBRARY_PATH
或PATH
)。
-
-
崩溃时的线程信息:
-
崩溃线程: 导致崩溃的线程的详细信息(通常是最核心的部分):
-
线程 ID。
-
线程状态(正在运行、等待等)。
-
该线程的完整本地栈和 Java 栈跟踪 (
Stack: [0x...]
和Java frames:
)。这能精确显示崩溃发生时该线程正在执行的代码位置(是本地库调用还是 Java 方法)。
-
-
所有其他线程: 列出 JVM 中所有其他线程的状态和堆栈信息(
"Thread"
开头的部分)。这有助于了解崩溃时整个应用程序的状态。
-
-
内存信息:
-
寄存器状态: 崩溃点 CPU 寄存器的值(非常底层,对分析 JVM bug 或本地代码 bug 有用)。
-
本地内存映射: 进程的虚拟内存区域列表 (
Memory:
部分),显示加载了哪些库(DLL/SO)以及它们的基地址。这对于分析内存访问冲突(如访问了未映射的内存或错误的库偏移)至关重要。 -
堆栈内存内容: 崩溃点附近栈内存的十六进制和 ASCII 转储。
-
核心转储信息: 如果生成了核心转储文件(core dump),会记录其位置。
-
堆内存摘要: (有时)包含 Heap 的简要信息(如分代大小、使用情况),但通常不是主要诊断点(因为 Heap OOM 一般不会导致 JVM 崩溃生成 hs_err)。
-
-
加载的模块信息:
-
列出所有加载的共享库(
.dll
,.so
)及其路径和基地址。这对于识别问题库非常关键。
-
-
环境变量: 进程的环境变量列表。
关键点总结:
-
触发条件: 当 JVM 自身遇到无法处理的致命错误(通常是底层原生代码问题、严重的资源耗尽如本地内存、JVM 内部错误、硬件问题等)而崩溃退出时生成。普通的 Java 异常(如
NullPointerException
)或 Java Heap 的OutOfMemoryError
不会触发生成此文件(它们只会打印到标准错误或应用日志)。 -
核心价值: 提供崩溃瞬间 JVM 和系统的完整快照,特别是崩溃线程的堆栈、寄存器、内存映射和详细的 JVM 配置。是诊断 JVM 崩溃、本地库(JNI)问题、操作系统兼容性问题或 JVM bug 的首要依据。
-
位置: 默认生成在 JVM 进程的当前工作目录下。有时也可能在操作系统的临时目录(如
/tmp
)或用户主目录。可以通过 JVM 启动参数-XX:ErrorFile=/path/to/file.log
指定生成位置。 -
分析: 分析该文件通常:
-
从文件头部快速定位崩溃原因(
EXCEPTION_ACCESS_VIOLATION
等)。 -
重点查看崩溃线程的堆栈信息(
Stack:
和Java frames:
),确定崩溃发生在 JVM 内部、JIT 编译的代码、还是你的 Java 代码调用的 JNI 本地方法。 -
检查
CommandLine flags
确认 JVM 参数(特别是内存设置)。 -
查看
Modules
或Dynamic libraries
部分确认是否有可疑的本地库。 -
结合
Memory
映射分析无效内存访问地址是否指向某个库。 -
检查是否有
OutOfMemoryError
提示本地内存耗尽。
-
当你在 Java 应用运行目录或日志目录发现 hs_err_pid.log
文件时,就明确表明 JVM 进程发生了严重的崩溃。这个文件是解决此类问题的关键起点。
我们在微服务的日志目录查看到hs_err_pid.log
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f656f438aec, pid=6245, tid=0x00007f6514546700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_201-b09) (build 1.8.0_201-b09)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C [libc.so.6+0x85aec] cfree+0x1c
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
--------------- T H R E A D ---------------
Current thread (0x00007f656808f000): ConcurrentGCThread [stack: 0x00007f6514447000,0x00007f6514547000] [id=6301]
siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x00007f629002cff8
Registers:
RAX=0x0000000000000000, RBX=0x00007f6445220930, RCX=0x0000000000000001, RDX=0x00007f629002d000
RSP=0x00007f6514545bf8, RBP=0x00000000ffffffff, RSI=0x00007f629002d000, RDI=0x00007f629002d000
R8 =0xffffffffffffffff, R9 =0x0000000000000000, R10=0x0000000000000022, R11=0x0000000000000246
R12=0x0000000000000000, R13=0x00007f6514545cf0, R14=0x0000000000000d70, R15=0x00007f656f776838
RIP=0x00007f656f438aec, EFLAGS=0x0000000000010206, CSGSFS=0x0000000000000033, ERR=0x0000000000000004
TRAPNO=0x000000000000000e
Top of Stack: (sp=0x00007f6514545bf8)
0x00007f6514545bf8: 00007f656f42f8ea 00007f6445220930
0x00007f6514545c08: 00007f656f42f055 00007f6445220930
0x00007f6514545c18: 000000000000001c 000000000000001c
0x00007f6514545c28: 00007f656f42d9c0 0000000000000009
0x00007f6514545c38: 0000000000000021 0000000000000035
0x00007f6514545c48: 00007f6445220930 000000000000001c
0x00007f6514545c58: 0000000000000001 000000000000001c
关于这个日志的解析:
1.以下这句说明了这个崩溃是在执行 cfree 时发生释放了一段未分配或者未映射的内存地址引起。
# Problematic frame:
# C [libc.so.6+0x85aec] cfree+0x1c
2.引起的原因是JVM的ConcurrentGCThread线程
Current thread (0x00007f656808f000): ConcurrentGCThread [stack: 0x00007f6514447000,0x00007f6514547000] [id=6301]
关于这个bug的修复可以参考官方文档:Bug ID: JDK-8191393 Random crashes during cfree+0x1c
大致原因是ConcurrentMarkThread与VMThread之间的竞态条件问题导致的。
修复的方案:升级JDK版本到8u261以上。
本人查看了线上的JVM版本是8u201,明显也是受影响的版本