Java程序员必学的Linux常用命令:日志排查与性能定位实战
1. 从图形界面到命令行Java程序员为什么绕不开Linux很多刚入行的Java开发者日常开发环境是Windows或者macOSIDE点几下就能跑起来Tomcat一启动日志刷刷地滚感觉一切都很美好。可一旦项目要部署到服务器或者线上出了故障需要登机器排查面对那个黑底白字的终端窗口瞬间就懵了。ls、cd、tail这些命令看着眼熟但组合起来用就心里没底更别提用grep配合awk去日志里捞关键信息了。这个项目标题叫“Java程序员入门技术大全V1(十九) -- Linux之常用命令”从编号“十九”能看出来这是一个系列教程中的一篇专门讲Linux常用命令。它的定位很明确面向Java程序员不是运维工程师也不是系统管理员。这意味着内容不需要覆盖Linux内核调优、网络协议栈分析这种深水区而是聚焦在Java开发日常工作中真正高频使用的那些命令——查看日志、定位进程、检查端口、管理文件、排查内存和CPU异常。为什么Java程序员要专门学Linux命令因为Java后端应用的最终运行环境绝大多数是Linux服务器。你本地写代码用Windows但代码跑在CentOS或者Ubuntu上。日志文件在/var/log或者应用自己的logs目录下进程是java -jar启动的端口是8080或者9090。出了问题运维可能只丢给你一句“服务挂了”剩下的排查工作得你自己上。这时候能不能熟练使用tail -f、grep、ps、netstat、jstack这些命令直接决定了你定位问题的速度。这篇文章适合谁看如果你是刚学Java不久或者一直在Windows环境下开发对Linux命令只有零散认知那这篇内容就是为你准备的。我会从Java程序员的实际工作场景出发把常用命令按用途分类讲清楚每个命令都配上真实会遇到的例子并且解释为什么这么用。不是罗列手册而是讲清楚每个命令解决什么问题、在什么场景下用、有哪些坑要避开。提示本文所有命令示例均基于常见的Linux发行版如CentOS 7/8、Ubuntu 20.04不同发行版在个别命令参数上可能有细微差异我会在涉及的地方特别说明。2. 文件与目录操作日志和配置文件的日常打交道Java程序员登服务器第一件事往往是找日志。应用日志、GC日志、启动日志都散落在不同的目录里。文件与目录操作命令是用得最频繁的一类没有之一。2.1 查看与切换ls、cd、pwd的实战用法ls命令看似简单但参数组合用好了能省很多事。最常用的几个参数ls -l以长格式列出显示权限、所有者、大小、修改时间。排查问题时你需要知道日志文件最后修改时间是什么时候这个参数必用。ls -lh在-l基础上把文件大小显示为人类可读格式KB、MB、GB。看日志文件大小时-h非常实用。ls -lt按修改时间倒序排列最新的文件排在最前面。日志目录下文件多的时候这个参数能让你一眼看到最新写入的文件。ls -ltr按修改时间正序排列最旧的在前。有时候你需要找最早的那个日志文件就用这个。组合起来我常用的命令是ls -lhtr先按时间正序排再显示人类可读大小最后长格式展示。这样从旧到新一目了然排查历史问题时特别顺手。cd命令没什么好说的但有几个快捷方式值得记住cd -回到上一次所在的目录。在两个目录之间来回切换时比反复输入路径快得多。cd ~回到当前用户的家目录。cd ..回到上一级目录。pwd命令用于显示当前所在目录的完整路径。当你cd了很多层之后不确定自己在哪敲一下pwd就清楚了。这个命令在写脚本时也常用用来获取当前工作目录。2.2 查找与搜索find和grep的组合拳find命令用于在目录树中查找文件功能极其强大。Java程序员最常用的场景是知道日志文件名的一部分但不知道具体在哪个目录下。# 在当前目录及子目录中查找所有以.log结尾的文件 find . -name *.log # 查找最近7天内修改过的.log文件 find . -name *.log -mtime -7 # 查找大于100MB的日志文件 find . -name *.log -size 100M # 查找并删除7天前的日志文件慎用建议先ls确认 find . -name *.log -mtime 7 -exec rm {} \;find的-exec参数可以配合其他命令使用但要注意{}和\;的写法。更安全的做法是先用find列出文件确认无误后再执行删除操作。grep是文本搜索的利器在日志排查中不可或缺。基本用法# 在文件中搜索包含Exception的行 grep Exception app.log # 递归搜索当前目录下所有文件 grep -r NullPointerException . # 显示匹配行的行号 grep -n ERROR app.log # 反向匹配显示不包含DEBUG的行 grep -v DEBUG app.log # 统计匹配行数 grep -c ERROR app.loggrep配合管道符|使用威力倍增。比如查看日志中最近的错误tail -n 1000 app.log | grep ERROR这条命令先取日志最后1000行再从中筛选包含ERROR的行。实际排查时我经常用grep加上-A、-B、-C参数来查看上下文grep -A 5 Exception app.log显示匹配行及其后5行grep -B 3 Exception app.log显示匹配行及其前3行grep -C 5 Exception app.log显示匹配行前后各5行异常堆栈信息往往跨多行用-A参数能看到完整的堆栈这对定位问题至关重要。2.3 文件内容查看cat、less、tail、head的选择策略查看文件内容有好几个命令用哪个取决于文件大小和你的目的。cat适合查看小文件或者把多个文件拼接输出。但用它看大日志文件就是灾难屏幕会疯狂滚动最后只看到末尾几行。cat更常见的用途是配合重定向# 清空文件内容慎用 cat /dev/null app.log # 合并多个文件 cat file1.log file2.log combined.logless是查看大文件的正确方式。它支持上下翻页、搜索、跳转而且不会一次性加载整个文件内存占用低。常用操作空格键向下翻一页b键向上翻一页/关键词向下搜索?关键词向上搜索n键跳到下一个匹配项N键跳到上一个匹配项G键跳到文件末尾g键跳到文件开头q键退出tail用于查看文件末尾内容最常用的参数是-f实时跟踪文件变化。Java应用启动时用tail -f catalina.out或者tail -f app.log盯着启动日志是每个后端开发的日常。# 实时跟踪日志文件 tail -f app.log # 显示最后100行并实时跟踪 tail -n 100 -f app.log # 跟踪多个文件会显示文件名分隔 tail -f app.log error.loghead与tail相反查看文件开头内容。默认显示前10行用-n指定行数# 查看文件前50行 head -n 50 app.log实际工作中tail -f和grep的组合使用频率最高。比如实时过滤错误日志tail -f app.log | grep --line-buffered ERROR注意--line-buffered参数它让grep逐行输出而不是等缓冲区满了才输出。不加这个参数你可能会发现日志半天不刷新以为程序卡住了。注意tail -f跟踪的是文件描述符如果日志文件被切割比如logrotatetail -f会继续跟踪旧文件。这时候需要用tail -F大写F它会跟踪文件名文件被切割后自动切换到新文件。3. 进程与端口管理定位Java应用的关键技能Java应用跑在服务器上本质就是一个Java进程。找到它、看它的状态、管理它的生命周期是排查问题的第一步。3.1 ps与top查看进程状态和资源占用ps命令用于查看当前系统的进程状态。Java程序员最常用的组合是# 查看所有Java进程 ps -ef | grep java # 或者用更简洁的写法 ps aux | grep javaps -ef和ps aux的区别在于输出格式略有不同-ef是System V风格aux是BSD风格。实际用哪个看个人习惯我一般用ps -ef因为输出中的PPID父进程ID列对排查进程关系有帮助。输出中需要关注的信息PID进程ID后续操作如kill、jstack都需要它PPID父进程ID可以知道这个Java进程是被谁启动的CMD启动命令能看到jar包路径、JVM参数等STIME启动时间TIME累计CPU时间top命令用于实时查看系统资源占用情况包括CPU、内存、进程等。Java程序员用top主要看两件事哪个进程占CPU高哪个进程占内存高。# 启动top top # 只查看特定进程 top -p 12345 # 按CPU使用率排序默认 top -o %CPU # 按内存使用率排序 top -o %MEM在top界面中按1可以展开查看每个CPU核心的使用情况按M按内存排序按P按CPU排序按q退出。对于Java应用top看到的高CPU进程往往需要进一步用top -Hp pid查看该进程内各个线程的CPU占用找到最耗CPU的线程ID再转换成十六进制用jstack去查对应的线程堆栈。这是排查CPU飙高问题的标准流程。3.2 netstat与ss端口占用排查Java应用启动失败最常见的原因之一就是端口被占用。netstat和ss命令用于查看网络连接和端口监听状态。# 查看所有监听端口 netstat -tlnp # 查看特定端口是否被占用 netstat -tlnp | grep 8080 # 查看所有TCP连接 netstat -anp | grep 8080参数说明-tTCP协议-l仅显示监听状态的连接-n以数字形式显示地址和端口不解析域名-p显示进程ID和程序名需要root权限ss命令是netstat的现代替代品速度更快输出更简洁# 查看所有监听端口 ss -tlnp # 查看特定端口 ss -tlnp | grep 8080如果发现端口被占用可以用lsof命令查看具体是哪个进程lsof -i:8080输出会显示进程名、PID、用户等信息。确认后可以用kill命令终止进程。3.3 kill与kill -9优雅停止与强制终止找到进程后需要停止它时用kill命令。kill默认发送SIGTERM信号15这是一个优雅停止的信号Java应用收到后会执行关闭钩子Shutdown Hook完成资源清理后再退出。# 优雅停止进程 kill 12345 # 强制终止进程发送SIGKILL信号9 kill -9 12345kill -9是最后手段因为它不给进程任何清理机会可能导致数据丢失或资源未释放。我踩过的坑有一次用kill -9强制杀了一个正在写文件的Java进程结果文件写了一半重启后数据不一致排查了很久。所以能用kill解决就不要用kill -9。如果进程不响应kill可以先尝试kill -15等几秒如果还在再考虑kill -9。另外pkill命令可以按名称批量杀进程# 杀掉所有名为java的进程慎用可能误杀其他Java应用 pkill java # 更精确的方式按完整命令行匹配 pkill -f myapp.jar注意pkill -f匹配的是完整命令行如果多个Java应用命令行相似可能误杀。生产环境建议先ps -ef | grep确认PID再用kill精确操作。4. 日志分析与文本处理从海量日志中捞关键信息Java应用的日志动辄几百MB甚至几个GB靠肉眼翻是不现实的。掌握几个文本处理命令能让你在几分钟内从日志中定位到关键信息。4.1 awk与sed文本提取和替换的利器awk适合按列处理文本sed适合按行进行替换和删除。这两个命令功能强大但Java程序员不需要学全掌握几个常用模式就够了。awk的典型用法# 提取日志中的时间戳和日志级别假设以空格分隔第1列是时间第2列是级别 awk {print $1, $2} app.log # 按条件过滤打印包含ERROR的行的第3列到最后一列 awk /ERROR/ {for(i3;iNF;i) printf %s , $i; print } app.log # 统计每个日志级别出现的次数 awk {print $2} app.log | sort | uniq -c | sort -rn最后一条命令特别实用先提取日志级别列排序后去重计数再按次数倒序排列。这样一眼就能看出ERROR、WARN、INFO各有多少条。sed的典型用法# 将文件中的old替换为new只输出到屏幕不修改原文件 sed s/old/new/g app.log # 直接修改原文件慎用建议先备份 sed -i s/old/new/g app.log # 删除包含DEBUG的行 sed /DEBUG/d app.log # 打印第100到200行 sed -n 100,200p app.logsed -n 100,200p这个用法在查看日志特定区间时很有用比head和tail组合更直观。4.2 sort、uniq、wc统计与去重sort用于排序uniq用于去重wc用于统计。这三个命令配合使用能快速完成日志统计。# 统计日志中不同IP的访问次数假设IP在第一列 awk {print $1} access.log | sort | uniq -c | sort -rn | head -20 # 统计文件行数 wc -l app.log # 统计文件字节数 wc -c app.loguniq -c统计连续重复行的次数所以必须先sort否则相同的行不相邻统计结果会出错。sort -rn按数值倒序排列head -20取前20条。这个组合是日志分析的万能模板把awk {print $1}换成其他列就能统计不同维度的数据。4.3 日志实时监控与关键字高亮生产环境排查问题时经常需要一边操作一边盯着日志。tail -f配合grep是标配但默认输出没有颜色关键字不突出。可以用grep --colorauto让匹配的关键字高亮显示tail -f app.log | grep --colorauto -E ERROR|WARN|Exception-E参数启用扩展正则表达式支持|表示“或”。这样ERROR、WARN、Exception都会高亮显示一眼就能看到。如果日志量特别大tail -f刷屏太快可以加上grep过滤只看自己关心的内容# 只看特定线程的日志 tail -f app.log | grep --colorauto thread-1 # 只看特定请求ID的日志 tail -f app.log | grep --colorauto requestIdabc123实操心得在分布式系统中一个请求可能经过多个服务每个服务都有自己的日志。排查问题时先拿到请求IDtraceId然后在各个服务的日志中用grep搜索这个ID把相关日志串起来看。这个技巧能大幅缩短排查时间。5. 系统资源与性能排查CPU、内存、磁盘、网络Java应用出问题有时候不是代码的锅而是服务器资源不够了。CPU跑满、内存耗尽、磁盘写满、网络不通都会导致应用异常。掌握几个系统资源查看命令能帮你快速判断问题方向。5.1 free与vmstat内存使用情况free命令查看系统内存使用情况free -h输出中的关键列total总内存used已用内存free空闲内存shared共享内存buff/cache缓冲和缓存available可用内存这是应用程序真正能用的内存看内存是否充足重点看available列而不是free列。因为Linux会把空闲内存用于缓存free少不代表内存不够available才是关键指标。vmstat命令可以查看虚拟内存统计信息包括进程、内存、交换分区、IO、CPU等vmstat 1 5参数1 5表示每秒采样一次共采样5次。输出中的关键列r运行队列中的进程数如果长期大于CPU核心数说明CPU不够用b阻塞的进程数si/so交换分区换入换出如果非0说明内存不够在用磁盘当内存us/sy/id用户态、系统态、空闲CPU时间百分比5.2 df与du磁盘空间排查磁盘写满是Java应用常见的故障原因之一。日志文件、临时文件、上传文件都可能把磁盘撑满。# 查看各分区使用情况 df -h # 查看当前目录下各子目录大小 du -sh * # 查看指定目录总大小 du -sh /var/log # 按大小排序显示子目录 du -sh * | sort -rh | head -10df -h看整体分区使用率du -sh *看具体目录占用。如果发现某个分区使用率100%用du逐层排查找到大文件或大目录。注意du统计的是文件占用的磁盘块大小可能和ls -l看到的文件大小略有差异。另外如果文件被删除但进程还在写du可能统计不到但df会显示空间未释放。这种情况需要重启相关进程才能释放空间。5.3 网络连通性ping、curl、telnet排查网络问题时ping、curl、telnet是最常用的三个命令。# 测试网络连通性 ping -c 4 www.example.com # 测试HTTP接口是否可达 curl -I http://localhost:8080/health # 测试端口是否开放 telnet localhost 8080ping测试主机是否可达-c 4表示发送4个包后停止。curl -I只获取HTTP响应头适合快速检查接口是否正常。telnet测试TCP端口是否开放如果连接成功会显示Connected失败则显示Connection refused或超时。curl还有更多实用参数# 发送POST请求 curl -X POST -H Content-Type: application/json -d {key:value} http://localhost:8080/api # 显示详细请求过程 curl -v http://localhost:8080/api # 将响应保存到文件 curl -o response.json http://localhost:8080/api在排查接口问题时curl -v能看到完整的请求头和响应头对定位问题很有帮助。6. 常见问题与排查技巧实录这一节整理Java程序员在使用Linux命令时最常遇到的问题和排查思路以速查表形式呈现方便快速参考。6.1 命令执行报错速查表问题现象可能原因排查命令解决方法Permission denied当前用户无权限ls -l查看文件权限用sudo提权或chmod修改权限No such file or directory路径错误或文件不存在ls确认路径检查路径拼写确认文件存在Command not found命令未安装或不在PATH中which 命令名安装对应软件包或使用绝对路径Connection refused目标端口未监听netstat -tlnp | grep 端口检查服务是否启动端口是否被占用Cannot allocate memory内存不足free -h释放内存或增加内存No space left on device磁盘写满df -h清理日志或临时文件Too many open files文件句柄数超限ulimit -n调整ulimit或修改系统配置6.2 日志排查的独家避坑技巧第一个坑grep搜索大文件时如果文件有几个GB直接grep会非常慢。可以先tail取最后一部分再greptail -n 100000 app.log | grep ERROR第二个坑tail -f跟踪的日志文件被切割后tail -f会失效。用tail -F替代它会自动跟踪新文件。第三个坑grep默认区分大小写搜索error不会匹配ERROR。用grep -i忽略大小写grep -i error app.log第四个坑日志中的时间戳格式多样用grep搜索特定时间段不方便。可以先用awk或sed提取时间列再过滤awk $1 2024-01-01 $1 2024-01-31 app.log第五个坑kill -9之后端口可能没有立即释放导致重启失败。这是因为TCP连接处于TIME_WAIT状态。可以用netstat -tlnp | grep 端口确认等待几十秒后再启动或者调整内核参数缩短TIME_WAIT时间。6.3 命令组合的实战案例案例一统计某个接口的QPS。假设日志中每行都有接口路径和请求时间可以用awk提取接口路径sort排序uniq -c计数再除以时间窗口grep /api/order access.log | awk {print $4} | cut -d: -f1-2 | sort | uniq -c案例二找出最耗CPU的Java线程。先用top -Hp pid找到高CPU线程ID转换成十六进制再用jstack查堆栈# 找到高CPU线程ID假设是12345 printf %x\n 12345 # 用jstack查看对应线程堆栈 jstack pid | grep -A 20 nid0x3039案例三实时监控日志中的异常并统计每分钟出现次数tail -f app.log | grep --line-buffered Exception | awk {print strftime(%Y-%m-%d %H:%M), $0} | uniq -c这个命令会实时输出每分钟的异常数量对观察故障趋势很有帮助。实操心得命令组合没有标准答案关键是理解每个命令的输入输出然后像搭积木一样组合起来。我刚开始学的时候喜欢把常用组合写成shell脚本或者alias比如alias logerrtail -f app.log | grep --colorauto -E ERROR|Exception这样每次排查问题少敲很多字。7. 从命令到脚本把重复操作自动化单个命令解决单次问题但Java程序员日常有很多重复操作比如每天检查日志、清理旧文件、监控服务状态。把这些操作写成shell脚本能省下大量时间。7.1 常用脚本模板日志清理脚本#!/bin/bash LOG_DIR/var/log/myapp DAYS7 find $LOG_DIR -name *.log -mtime $DAYS -exec rm -f {} \; echo Cleaned logs older than $DAYS days in $LOG_DIR服务健康检查脚本#!/bin/bash PID$(ps -ef | grep myapp.jar | grep -v grep | awk {print $2}) if [ -z $PID ]; then echo Service is down, restarting... nohup java -jar /opt/myapp/myapp.jar /var/log/myapp/startup.log 21 else echo Service is running with PID: $PID fi日志关键字监控脚本#!/bin/bash LOG_FILE/var/log/myapp/app.log KEYWORDSERROR|Exception|OutOfMemory tail -f $LOG_FILE | while read line; do if echo $line | grep -qE $KEYWORDS; then echo [ALERT] $(date %Y-%m-%d %H:%M:%S) $line fi done这些脚本可以直接用也可以根据自己的需求修改。写脚本时注意加注释方便以后维护。7.2 脚本调试与权限管理写好的脚本需要加执行权限chmod x script.sh调试脚本时用bash -x script.sh可以打印每条执行的命令方便定位问题。脚本中的变量引用建议加双引号比如$PID而不是$PID避免变量为空时命令出错。注意生产环境的脚本一定要先在测试环境验证特别是涉及删除、重启操作的脚本。我见过有人写了个清理脚本结果路径变量没设置好把整个目录删了教训惨痛。8. 命令学习的方法论从死记硬背到融会贯通Linux命令有几百个常用的也有几十个靠死记硬背不现实。我的经验是按场景学而不是按命令学。先明确你作为Java程序员最常遇到的场景看日志、查进程、看端口、排查CPU和内存、清理磁盘。每个场景下需要哪几个命令把这几条命令练熟就覆盖了80%的日常工作。然后理解命令的设计哲学Linux命令大多遵循“一个命令只做一件事做好”的原则。grep只管搜索sort只管排序awk只管按列处理。它们通过管道|组合形成强大的处理链。理解了这个哲学你就能灵活组合命令而不是死记硬背每个命令的所有参数。最后多用man和--help。遇到不熟悉的命令先man 命令名看手册或者命令名 --help看快速帮助。手册虽然枯燥但信息最全。我习惯用tldr工具如果服务器上有的话它用示例的方式展示命令的常用用法比man更直观。提示如果服务器上没有tldr可以用man配合/搜索。在man页面中按/输入关键词按n跳到下一个匹配项。这个技巧能帮你在长长的手册中快速找到需要的参数。学命令最好的方式是用。每次登服务器排查问题刻意用新学的命令去操作用几次就记住了。我刚开始学awk的时候每次看日志都强迫自己用awk提取列用了不到一周就熟练了。命令是工具用多了自然就顺手了。