Java 服务跑在 CentOS 上时,报错往往不只是一行 ERROR 那么简单。真正高效的排查方式,是先确认进程和日志落点,再用系统资源、JVM 线索、框架配置和线程内存工具逐层收缩范围。下面这套流程适合从临时故障到持续性异常的多数场景,读完你可以判断问题更像是日志配置、系统瓶颈,还是 JVM 与代码层面的异常。
先确认进程和日志文件在哪
排查的第一步,不是急着搜异常栈,而是先确认当前到底是哪个 Java 进程在运行、它把日志写到了哪里。很多定位失败,问题就出在这一步拿错了 PID,或者盯错了日志文件。

可以先用下面的命令列出 Java 进程,确认 PID 和启动参数:
ps -ef | grep java
启动参数里经常能直接看到日志配置,例如:
-Dlogging.file.name:常见于 Logback、Log4j2;-Djava.util.logging.config.file:常见于 JUL 配置。
如果命令行里没有直接写出日志路径,就继续看应用部署目录下的 logs 文件夹,或者容器与中间件的默认输出位置。例如 Tomcat 场景下,常见文件就是 catalina.out。如果是 Spring Boot 项目,还要顺手检查 application.properties,像下面这种配置很常见:
logging.file.name=logs/app.log
这一步的目标很明确:先把“哪个进程”和“哪个日志文件”对上号,后面的实时跟踪、错误过滤和资源关联分析才不会跑偏。
实时看错误,再把时间线对齐
日志路径确定后,下一步就是快速把噪音压下去,只盯真正有价值的异常信息。CentOS 上最常用的组合还是 tail 和 grep。
几条最常用的命令可以直接上手:
grep "ERROR" /path/to/java.log:先把所有错误级别日志提出来;grep -i "exception" /path/to/java.log:忽略大小写,抓取各类异常;tail -f /path/to/java.log | grep --color=auto "ERROR":一边实时跟踪,一边高亮错误行。
如果你已经知道服务是在某个时间点开始报错,排查效率会更高。做法不是只盯一行 ERROR,而是把错误前后的上下文一起看清楚:报错前是否有请求激增、连接失败、配置加载异常,报错后是否连续出现相同栈信息。很多时候,真正的根因并不是第一条显眼的异常,而是它之前几秒钟出现的告警。
对于托管为系统服务的 Java 应用,CentOS 7 及以上版本还可以配合 journalctl 把服务日志和应用日志对齐来看:
journalctl -u java_service_namejournalctl -u java_service_name --since "1 hour ago"journalctl -u java_service_name | grep "ERROR"
这样做的价值在于,应用日志只能说明“程序自己看到了什么”,系统日志则能补上“服务是怎么被启动、重启、中断或拉起失败的”。两边时间线一对,很多问题会快很多浮出来。
日志报错时,先排除是不是系统资源扛不住
Java 应用报错,不代表一定是业务代码出问题。在线上环境里,CPU、内存和磁盘空间吃紧,往往也会直接表现成超时、频繁 Full GC、服务无响应,甚至进程被系统杀掉。

CPU 是否异常打满
先看 CPU 使用情况:

top,然后按P按 CPU 占用排序;htop,如果环境里已安装,会更直观。
如果某个 Java 进程长时间超过 80%,就要进一步判断是正常流量高峰,还是线程空转、死循环、频繁 GC 导致的异常消耗。
内存是否已经告急
内存问题是 Java 故障中最常见的一类。可以先看整体内存使用:
free -m
重点关注文中提到的 -/+ buffers/cache 一行,再结合下面的命令持续观察:
vmstat 1 5
这里要重点看 si/so 两列。如果交换分区频繁读写,通常说明物理内存已经很紧张,JVM 很可能会因为内存压力出现停顿、响应变慢,甚至抛出 OutOfMemoryError。
磁盘空间是否影响写日志
磁盘空间同样不能忽略:
df -h
如果某个分区的 Use% 接近 100%,日志写入、临时文件生成和转储文件落盘都可能出问题。表现出来,可能是应用突然不再输出日志,也可能是异常文件没能正常生成,导致排查信息反而更少。
此外还要记得查看内核日志:
dmesg
如果看到类似 Out of memory: Kill process 的信息,说明不是 Java 自己优雅退出,而是系统因为内存不足直接把进程杀掉了。这类问题,根因通常优先在系统资源,而不是业务代码本身。
怀疑 JVM 层面时,该看哪些文件和工具
当错误已经不像普通业务异常,而更像 GC 异常、进程崩溃、线程阻塞或内存泄漏时,就该进入 JVM 级别的排查了。
先看 GC 日志
如果应用启动时带上了下面这些参数,就能拿到比较完整的 GC 信息:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/app/gc.log
有了 GC 日志之后,可以用 VisualVM 或 GCViewer 来分析。如果发现 Full GC 很频繁,或者单次耗时明显偏长,就要继续判断:
- 堆内存是不是设置得过小;
-Xms与-Xmx是否合理;- 是否存在对象生命周期过长、无法及时回收的问题。
进程崩溃时看 hs_err 文件
如果 Java 进程直接异常退出,通常会留下 hs_err_pid<pid>.log。这个文件常见于 /var/log/java/ 或进程工作目录中,里面会记录:
- 崩溃类型,例如
OutOfMemoryError、StackOverflowError; - 线程堆栈;
- JVM 版本与运行环境信息。
这类文件的价值很高,因为它能把“进程为什么直接挂掉”讲得比普通应用日志更清楚。
线程阻塞用 jstack
如果现象是接口卡死、吞吐量下降、服务没有崩但响应很慢,可以抓线程转储:
jstack <PID>
再配合 FastThread 或 TDA 分析。如果发现大量线程处于 BLOCKED 状态,通常要回头检查同步代码,比如 synchronized 代码块、锁竞争或线程池使用方式。
内存泄漏用 jmap 和 MAT
怀疑堆内存泄漏时,可以先看堆使用概况:
jmap -heap <PID>
需要做进一步分析时,导出堆转储文件:
jmap -dump:format=b,file=heap.hprof <PID>
然后用 MAT(Memory Analyzer Tool)分析,找出占用内存最多的对象和可疑引用链。这一步往往能直接回答一个关键问题:到底是缓存没清、集合持续膨胀,还是某类对象被错误持有,导致内存无法释放。
别忽略日志框架冲突、级别设置和日志轮转
有些“日志问题”其实不是日志内容有问题,而是日志压根没有按预期输出。这时要检查的重点,是框架实现、配置文件和文件轮转策略。
先排查日志框架有没有打架
Java 项目里常见的日志组合包括 Log4j、Logback、SLF4J。多个实现同时存在时,最常见的结果就是输出混乱、日志丢失,或者根本没写到预期文件。
建议先确认项目里只保留一个日志实现框架,再检查配置文件是否正确,例如:
- Logback 的
logback.xml; - Log4j2 的
log4j2.xml。
重点关注两个地方:
<root level="INFO">这类根日志级别是否合理;<appender>是否指向了正确的日志文件路径。
依赖冲突则可以用 Maven 直接检查:
mvn dependency:tree
如果发现重复日志实现或历史包混入,可以通过 <exclusions> 排除,比如排掉 commons-logging,减少桥接和绑定混乱。
日志级别要按场景调整
日志级别不应该一成不变。开发和测试环境更适合 DEBUG 或 TRACE,方便观察详细行为;生产环境通常用 INFO 或 WARN,控制日志量并保留关键事件。
如果线上问题需要临时放大细节,可以修改日志框架配置文件,例如调整 logback.xml,也可以通过 JMX 动态调整,避免为了看日志而重启服务。
日志文件过大时用 logrotate
当单个日志文件大到 1GB 以上时,检索效率会明显下降,连 grep 都会变慢。这时候就该把日志轮转配置起来。
编辑 /etc/logrotate.d/java,可参考以下配置:
/path/to/java/logs/*.log {
daily
rotate 7
compress
missingok
notifempty
copytruncate
}
配置完成后,可以先做测试:
logrotate -d /etc/logrotate.d/javalogrotate -f /etc/logrotate.d/java
其中 -d 用于演练检查,-f 用于手动强制执行。对于仍在持续写入的日志文件,copytruncate 这种策略在很多场景下会比较实用。
分布式场景下,尽量别只靠手工翻日志
如果服务已经不是单机应用,而是多实例、分布式部署,单靠 SSH 登录服务器再用 grep 人肉筛日志,成本会迅速升高。这时应该把日志收集、索引和检索能力补起来。
常见方案里,ELK Stack 依然是经典组合:
- Logstash 负责收集日志;
- Elasticsearch 负责存储与索引;
- Kibana 负责检索和可视化。
Graylog 也很适合日志聚合和告警,例如当 ERROR 日志数量超过阈值时自动发邮件。资源更紧张的服务器,则可以考虑 Filebeat 这类更轻量的采集器。
这类工具的核心价值,不只是“看日志更方便”,而是能把多台机器、多类服务的异常放到同一个时间轴上。对于链路故障、级联超时、某一节点反复报错这类问题,集中分析通常比单机排查快得多。
一套更实用的排查顺序
把上面的内容串起来,CentOS 上排查 Java 日志错误时,可以按这个顺序推进:
- 先用
ps -ef | grep java确认目标进程和启动参数; - 找到真实日志文件位置,结合
tail -f、grep抓住异常时间点; - 同时检查
top、free -m、vmstat 1 5、df -h、dmesg,排除资源瓶颈; - 怀疑 JVM 层面时,继续看 GC 日志、
hs_err_pid<pid>.log、线程转储和堆转储; - 如果日志输出本身异常,再回头检查日志框架冲突、级别和轮转配置;
- 多实例环境则尽早接入 ELK、Graylog 或 Filebeat,减少手工排查成本。
这套方法的重点,不是一次把所有工具都用上,而是每一步都在缩小范围。只要能先判断问题属于“日志定位错误”“系统资源不足”“JVM 异常”还是“日志框架配置问题”,后续动作就会清晰很多。













