Java 线上问题排查与持续优化实战指南
一、基础知识
(一)Java 线上问题分类
在 Java 应用程序的线上环境中,问题多种多样,但通常可以归纳为以下几类:
- 性能问题
- 响应时间过长
- 问题表现:用户操作后,系统响应缓慢,用户体验差。
- 可能原因:代码逻辑复杂、数据库查询效率低下、网络延迟等。
- 示例代码:
上述代码中,public List<User> getAllUsers() { List<User> users = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { User user = new User(); user.setId(i); user.setName("User" + i); users.add(user); } return users; }
getAllUsers
方法在循环中创建了大量对象,导致响应时间过长。
- 吞吐量不足
- 问题表现:系统无法在高并发场景下处理大量请求。
- 可能原因:线程池配置不合理、资源瓶颈(如 CPU、内存、磁盘 I/O)或架构设计缺陷。
- 示例代码:
上述代码中,线程池大小固定为 10,当任务数量过多时,会导致吞吐量不足。public class FixedThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 1000; i++) { executor.submit(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } executor.shutdown(); } }
- 资源利用率过高
- 问题表现:CPU、内存、磁盘或网络资源使用率过高,可能导致系统性能下降甚至崩溃。
- 可能原因:程序存在大量计算密集型或 I/O 密集型操作。
- 示例代码:
上述代码中,public class CPUIntensiveTask { public static void main(String[] args) { while (true) { // 模拟 CPU 密集型任务 for (int i = 0; i < 1000000000; i++) { Math.sin(i); } } } }
CPUIntensiveTask
类中的main
方法会持续占用 CPU 资源,导致 CPU 使用率过高。
- 响应时间过长
- 异常问题
- 运行时异常
- 问题表现:程序运行过程中抛出异常,导致程序中断或行为异常。
- 可能原因:代码逻辑错误或数据质量问题。
- 示例代码:
上述代码中,public class NullPointerExample { public static void main(String[] args) { String str = null; System.out.println(str.length()); } }
str
为null
,调用str.length()
时会抛出NullPointerException
。
- 业务逻辑异常
- 问题表现:用户输入不符合预期或业务规则冲突,导致程序行为异常。
- 可能原因:业务逻辑设计不合理或用户输入未进行严格校验。
- 示例代码:
上述代码中,public class BusinessLogicExample { public static void main(String[] args) { int age = -1; if (age < 0) { throw new IllegalArgumentException("年龄不能为负数"); } } }
age
为负数,不符合业务逻辑,抛出IllegalArgumentException
。
- 系统级异常
- 问题表现:程序运行过程中抛出系统级异常,导致程序崩溃。
- 可能原因:JVM 配置不合理或代码存在严重问题。
- 示例代码:
上述代码中,程序不断创建对象并添加到public class OutOfMemoryExample { public static void main(String[] args) { List<Object> list = new ArrayList<>(); while (true) { list.add(new Object()); } } }
list
中,最终导致OutOfMemoryError
。
- 运行时异常
- 资源泄漏问题
- 内存泄漏
- 问题表现:程序运行过程中内存占用不断增加,最终可能引发
OutOfMemoryError
。 - 可能原因:程序中存在未正确释放的内存对象。
- 示例代码:
上述代码中,public class MemoryLeakExample { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { while (true) { list.add(new Object()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
list
不断添加对象,但从未释放,导致内存泄漏。
- 问题表现:程序运行过程中内存占用不断增加,最终可能引发
- 文件句柄泄漏
- 问题表现:程序运行过程中不断打开文件句柄,但未正确关闭,最终导致
Too many open files
错误。 - 可能原因:程序中未正确关闭文件句柄。
- 示例代码:
上述代码中,虽然使用了public class FileHandleLeakExample { public static void main(String[] args) { while (true) { try (FileInputStream fis = new FileInputStream("example.txt")) { fis.read(); } catch (IOException e) { e.printStackTrace(); } } } }
try-with-resources
语句来自动关闭文件句柄,但如果文件不存在或其他异常发生,可能会导致文件句柄泄漏。
- 问题表现:程序运行过程中不断打开文件句柄,但未正确关闭,最终导致
- 数据库连接泄漏
- 问题表现:程序运行过程中不断获取数据库连接,但未正确关闭,最终导致数据库连接池耗尽。
- 可能原因:程序中未正确关闭数据库连接。
- 示例代码:
上述代码中,未正确关闭数据库连接public class DatabaseConnectionLeakExample { public static void main(String[] args) { while (true) { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/example", "root", "password"); try { Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users"); while (rs.next()) { System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } } } }
conn
,导致数据库连接泄漏。
- 内存泄漏
(二)监控与日志的重要性
监控与日志是 Java 线上问题排查的基石,它们提供了问题排查所需的线索和数据支持。
- 监控的作用
- 实时性能监控
- 工具:Prometheus、Grafana 等。
- 示例:
- Prometheus 配置:
global: scrape_interval: 15s scrape_configs: - job_name: 'java_app' static_configs: - targets: ['localhost:8080']
- Grafana 配置:在 Grafana 中创建数据源并导入 Prometheus 数据,创建仪表盘以实时监控 CPU、内存等指标。
- Prometheus 配置:
- 应用性能监控
- 工具:Apache Skywalking、Pinpoint 等。
- 示例:
- Skywalking 配置:
agent: collector: backend_service: "127.0.0.1:11800"
- Pinpoint 配置:
pinpoint.agentId=java_app pinpoint.applicationName=java_app pinpoint.profiler.transport=GRPC pinpoint.collector.ip=127.0.0.1 pinpoint.collector.port=9999
- Skywalking 配置:
- 告警与通知
- 工具:Prometheus Alertmanager、Grafana Alerting 等。
- 示例:
- Prometheus Alertmanager 配置:
global: resolve_timeout: 5m route: receiver: 'webhook' receivers: - name: 'webhook' webhook_configs: - url: 'http://localhost:9093/webhook'
- Grafana Alerting 配置:在 Grafana 中创建告警规则,当 CPU 使用率超过 80% 时触发告警。
- Prometheus Alertmanager 配置:
- 实时性能监控
- 日志的作用
- 记录问题线索
- 工具:Log4j、SLF4J 等。
- 示例:
- Log4j 配置:
<Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="debug"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
- 日志记录:
private static final Logger logger = LogManager.getLogger(Example.class); public static void main(String[] args) { logger.info("Application started"); try { int result = 10 / 0; } catch (ArithmeticException e) { logger.error("ArithmeticException occurred", e); } }
- Log4j 配置:
- 定位问题根源
- 工具:ELK 堆栈(Elasticsearch、Logstash、Kibana)。
- 示例:
- Logstash 配置:
input { file { path => "/var/log/app.log" start_position => "beginning" } } output { elasticsearch { hosts => ["localhost:9200"] index => "app-logs" } }
- Kibana 查询:在 Kibana 中使用查询语句
error
查找错误日志。
- Logstash 配置:
- 分析问题趋势
- 工具:Grafana Loki。
- 示例:
- Loki 配置:
config: compactor: working_directory: /data/loki/boltdb-shipper-active shared_store: aws compaction_interval: 1h retention_enabled: true retention_delete_delay: 2h retention_delete_worker_count: 150
- Grafana 查询:在 Grafana 中创建日志面板,分析日志趋势。
- Loki 配置:
- 记录问题线索
二、问题定位方法
(一)业务日志分析
业务日志是 Java 应用程序运行过程中记录的与业务逻辑相关的日志信息,通过分析业务日志,可以快速找到问题的具体位置和原因。
- 日志收集与存储
- 日志收集工具:Logstash、Fluentd。
- 示例:
- Logstash 配置:
input { file { path => "/var/log/app.log" start_position => "beginning" } } output { elasticsearch { hosts => ["localhost:9200"] index => "app-logs" } }
- Fluentd 配置:
<source> @type tail path /var/log/app.log pos_file /var/log/app.log.pos tag app.log </source> <match app.log> @type elasticsearch host localhost port 9200 logstash_format true </match>
- Logstash 配置:
- 日志分析工具
- ELK 堆栈:Elasticsearch、Logstash、Kibana。
- 示例:
- Kibana 查询:在 Kibana 中使用查询语句
error
查找错误日志。 - Kibana 可视化:创建柱状图或折线图,分析错误日志的频率和趋势。
- Kibana 查询:在 Kibana 中使用查询语句
- Splunk:
- 示例:
- 数据索引:将日志数据索引到 Splunk 中。
- 查询语句:使用 Splunk 的查询语句
index=app error
查找错误日志。
- 示例:
- 开源日志分析工具:Graylog、Loggly。
- 示例:
- Graylog 配置:
input { file { path => "/var/log/app.log" start_position => "beginning" } } output { graylog { host => "localhost" port => 12201 } }
- Graylog 配置:
- 日志分析方法
- 关键字搜索:
- 示例:
- Elasticsearch 查询:
GET /app-logs/_search { "query": { "match": { "message": "error" } } }
- Elasticsearch 查询:
- 示例:
- 日志关联分析:
- 示例:
- Elasticsearch 查询:
GET /app-logs/_search { "query": { "bool": { "must": [ { "match": { "message": "error" }}, { "match": { "user_id": "12345" }} ] } } }
- Elasticsearch 查询:
- 示例:
- 日志统计分析:
- 示例:
- Elasticsearch 聚合查询:
GET /app-logs/_search { "size": 0, "aggs": { "error_count": { "terms": { "field": "error_level.keyword" } } } }
- Elasticsearch 聚合查询:
- 示例:
- 关键字搜索:
(二)APM 分析
APM(Application Performance Management)工具通过监控应用程序的性能指标和调用链路,帮助快速定位分布式架构中的问题。
- APM 工具的选择
- 开源 APM 工具:Apache Skywalking、Pinpoint。
- 示例:
- Skywalking 配置:
agent: collector: backend_service: "127.0.0.1:11800"
- Pinpoint 配置:
pinpoint.agentId=java_app pinpoint.applicationName=java_app pinpoint.profiler.transport=GRPC pinpoint.collector.ip=127.0.0.1 pinpoint.collector.port=9999
- Skywalking 配置:
- APM 工具的使用
- 性能指标监控:
- 示例:
- Skywalking Dashboard:在 Skywalking 的 Dashboard 中查看服务的响应时间、吞吐量、错误率等性能指标。
- Pinpoint Dashboard:在 Pinpoint 的 Dashboard 中查看服务的性能指标和调用链路。
- 示例:
- 链路追踪:
- 示例:
- Skywalking 链路追踪:在 Skywalking 的 Dashboard 中查看某个请求的调用链路,分析每个服务的响应时间和调用顺序。
- Pinpoint 链路追踪:在 Pinpoint 的 Dashboard 中查看某个请求的调用链路,分析每个服务的响应时间和调用顺序。
- 示例:
- 分布式追踪:
- 示例:
- Skywalking 分布式追踪:在 Skywalking 的 Dashboard 中查看跨服务的分布式调用链路,分析服务之间的调用关系和性能瓶颈。
- Pinpoint 分布式追踪:在 Pinpoint 的 Dashboard 中查看跨服务的分布式调用链路,分析服务之间的调用关系和性能瓶颈。
- 示例:
- 性能指标监控:
(三)外部环境排查
外部环境问题可能导致 Java 应用程序运行异常,因此需要对系统环境进行排查。
- CPU 分析
- 工具使用:
top
、htop
。 - 示例:
- 命令:
在top
top
命令的输出中,查看 CPU 使用率较高的进程。 - 线程分析:
使用jstack -l <pid> > thread_dump.txt
jstack
获取线程栈信息,分析是否存在死循环或热点代码。
- 命令:
- 工具使用:
- 内存分析
- 工具使用:
free -m
、vmstat
。 - 示例:
- 命令:
查看系统的内存使用情况。free -m
- JVM 内存分析:
查看 JVM 的堆内存使用情况。jmap -heap <pid>
- 命令:
- 工具使用:
- 磁盘分析
- 工具使用:
df -h
、iostat
。 - 示例:
- 命令:
查看磁盘的使用率。df -h
- 文件系统分析:
查看文件系统的挂载情况。mount
- 命令:
- 工具使用:
- 网络分析
- 工具使用:
dstat
、netstat
。 - 示例:
- 命令:
查看端口 8080 的网络连接情况。netstat -an | grep :8080
- 网络配置检查:
查看主机名解析配置。cat /etc/hosts
- 命令:
- 工具使用:
(四)应用服务排查
Java 应用服务的内部问题可能导致性能下降或运行异常,需要对应用服务进行详细排查。
- CPU 分析
- 线程栈分析:
- 示例:
获取线程栈信息,分析是否存在死循环或热点代码。jstack -l <pid> > thread_dump.txt
- 示例:
- 热点代码定位:
- 示例:
查找处于运行状态的线程,定位热点代码。jstack -l <pid> | grep "java.lang.Thread.State: RUNNABLE"
- 示例:
- 线程栈分析:
- 内存分析
- 堆内存分析:
- 示例:
导出 JVM 堆内存,使用工具(如 MAT)分析内存泄漏或对象分布情况。jmap -dump:format=b,file=heapdump.hprof <pid>
- 示例:
- 内存泄漏排查:
- 示例:
查看存活对象的统计信息,分析是否存在内存泄漏。jmap -histo:live <pid>
- 示例:
- 堆内存分析:
- 线程分析
- 线程状态分析:
- 示例:
查找处于阻塞状态的线程,分析是否存在线程阻塞问题。jstack -l <pid> | grep "java.lang.Thread.State: BLOCKED"
- 示例:
- 线程池配置优化:
- 示例:
将线程池大小调整为 20,以优化线程池配置。public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(20); for (int i = 0; i < 1000; i++) { executor.submit(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } executor.shutdown(); } }
- 示例:
- 线程状态分析:
- 垃圾回收分析
- 垃圾回收日志分析:
- 示例:
启用垃圾回收日志,分析垃圾回收的频率和耗时。java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:LogFile=gc.log -jar yourapp.jar
- 示例:
- 垃圾回收调优:
- 示例:
使用 G1 垃圾回收器,调整垃圾回收参数以优化性能。java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4M -jar yourapp.jar
- 示例:
- 垃圾回收日志分析:
(五)数据库分析
数据库是 Java 应用程序的重要组成部分,数据库性能问题可能导致应用程序响应缓慢或运行异常。
- 数据库性能监控
- 性能指标监控:
- 示例:
- MySQL 监控:
SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Queries';
- Oracle 监控:
SELECT COUNT(*) FROM v$session WHERE status = 'ACTIVE';
- MySQL 监控:
- 示例:
- 慢查询日志分析:
- 示例:
- MySQL 慢查询日志:
查看慢查询日志,分析执行时间较长的 SQL 语句。SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;
- MySQL 慢查询日志:
- 示例:
- 性能指标监控:
- 数据库执行计划分析
- 执行计划查看:
- 示例:
- MySQL 执行计划:
EXPLAIN SELECT * FROM users WHERE age > 30;
- Oracle 执行计划:
EXPLAIN PLAN FOR SELECT * FROM users WHERE age > 30; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
- MySQL 执行计划:
- 示例:
- 执行计划优化:
- 示例:
- 优化 SQL 语句:
添加索引以优化查询性能。SELECT * FROM users WHERE age > 30 AND status = 'active';
- 优化 SQL 语句:
- 示例:
- 执行计划查看:
- 数据库锁分析
- 锁等待事件分析:
- 示例:
- MySQL 锁等待:
查看锁等待事件,分析是否存在锁冲突。SHOW ENGINE INNODB STATUS;
- Oracle 锁等待:
SELECT * FROM v$lock WHERE type = 'TM';
- MySQL 锁等待:
- 示例:
- 锁优化:
- 示例:
- 优化事务逻辑:
减少事务的持有时间,避免锁冲突。BEGIN TRANSACTION; UPDATE users SET status = 'active' WHERE age > 30; COMMIT;
- 优化事务逻辑:
- 示例:
- 锁等待事件分析:
- 数据库配置优化
- 配置参数调整:
- 示例:
- MySQL 配置:
[mysqld] innodb_buffer_pool_size = 2G innodb_log_file_size = 512M
- Oracle 配置:
ALTER SYSTEM SET DB_CACHE_SIZE = 2G;
- MySQL 配置:
- 示例:
- 数据库升级与优化:
- 示例:
- 升级 MySQL:
sudo apt-get install mysql-server
- 优化数据库架构:
ALTER TABLE users ADD INDEX idx_age_status (age, status);
- 升级 MySQL:
- 示例:
- 配置参数调整:
(六)网络分析
网络问题是影响 Java 应用程序性能和稳定性的重要因素之一,需要对网络环境进行详细分析。
- 网络性能监控
- 性能指标监控:
- 示例:
- 使用
iftop
监控网络带宽:sudo apt-get install iftop iftop -i eth0
- 使用
ping
测试网络延迟:ping -c 10 google.com
- 使用
- 示例:
- 网络流量分析:
- 示例:
- 使用
tcpdump
捕获网络流量:sudo tcpdump -i eth0 -w network_traffic.pcap
- 使用 Wireshark 分析网络流量:
wireshark network_traffic.pcap
- 使用
- 示例:
- 性能指标监控:
- 网络配置检查
- 防火墙规则检查:
- 示例:
- 查看防火墙规则:
sudo iptables -L
- 添加防火墙规则:
sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
- 查看防火墙规则:
- 示例:
- 路由配置检查:
- 示例:
- 查看路由配置:
ip route show
- 添加路由:
sudo ip route add 192.168.1.0/24 via 192.168.0.1
- 查看路由配置:
- 示例:
- 防火墙规则检查:
- 网络连通性测试
- 连通性测试工具:
- 示例:
- 使用
ping
测试网络连通性:ping -c 4 8.8.8.8
- 使用
traceroute
测试网络路径:traceroute 8.8.8.8
- 使用
- 示例:
- DNS 配置检查:
- 示例:
- 查看 DNS 配置:
cat /etc/resolv.conf
- 测试 DNS 解析:
nslookup google.com
- 查看 DNS 配置:
- 示例:
- 连通性测试工具:
三、常用工具
在 Java 线上问题排查过程中,使用合适的工具可以大大提高排查效率。
(一)日志分析工具
- ELK 堆栈
- Elasticsearch:用于存储和索引日志数据,支持高效的全文搜索和数据分析。
- Logstash:用于日志收集和解析,支持多种日志格式和数据源。
- Kibana:用于日志可视化和分析,提供丰富的可视化组件和查询功能。
- 示例:
- Logstash 配置:
input { file { path => "/var/log/app.log" start_position => "beginning" } } output { elasticsearch { hosts => ["localhost:9200"] index => "app-logs" } }
- Kibana 查询:
GET /app-logs/_search { "query": { "match": { "message": "error" } } }
- Logstash 配置:
- Splunk
- 日志收集与索引:支持多种日志源的收集和索引,提供强大的日志处理能力。
- 日志搜索与分析:支持复杂的日志搜索和分析功能,提供丰富的报表和可视化功能。
- 告警与通知:支持根据预设的规则触发告警,及时通知相关人员。
- 示例:
- 数据索引:将日志数据索引到 Splunk 中。
- 查询语句:
index=app error
- 其他日志分析工具
- Graylog:开源的日志分析工具,支持日志收集、索引、搜索和可视化功能。
- Loggly:云原生的日志分析工具,支持日志收集、索引、搜索和可视化功能,适合中小规模的系统。
- 示例:
- Graylog 配置:
input { file { path => "/var/log/app.log" start_position => "beginning" } } output { graylog { host => "localhost" port => 12201 } }
- Graylog 配置:
(二)APM 工具
- Apache Skywalking
- 性能监控:支持 Java、Node.js 等多种语言的性能监控,提供丰富的性能指标和链路追踪功能。
- 链路追踪:支持分布式链路追踪,可以查看每个请求的调用链路和性能瓶颈。
- 可视化界面:提供直观的可视化界面,方便用户查看性能指标和链路追踪信息。
- 示例:
- Skywalking 配置:
agent: collector: backend_service: "127.0.0.1:11800"
- 链路追踪查询:在 Skywalking 的 Dashboard 中查看某个请求的调用链路。
- Skywalking 配置:
- Pinpoint
- 性能监控:支持 Java 应用程序的性能监控,提供详细的性能指标和链路追踪功能。
- 链路追踪:支持分布式链路追踪,可以查看每个请求的调用链路和性能瓶颈。
- 实时监控:支持实时监控功能,可以实时查看应用程序的性能指标和链路追踪信息。
- 示例:
- Pinpoint 配置:
pinpoint.agentId=java_app pinpoint.applicationName=java_app pinpoint.profiler.transport=GRPC pinpoint.collector.ip=127.0.0.1 pinpoint.collector.port=9999
- 链路追踪查询:在 Pinpoint 的 Dashboard 中查看某个请求的调用链路。
- Pinpoint 配置:
- 商业 APM 工具
- New Relic:提供全面的性能监控和分析功能,支持多种语言和框架。
- AppDynamics:提供强大的性能监控和分析功能,支持多种语言和框架。
- 示例:
- New Relic 配置:
sudo apt-get install newrelic-daemon
- AppDynamics 配置:
sudo apt-get install appdynamics-agent
- New Relic 配置:
(三)系统监控工具
- Prometheus
- 指标收集:支持多种指标收集方式,可以收集系统性能指标、应用程序性能指标等。
- 指标存储:支持高效的指标存储和查询功能,可以存储大量的性能指标数据。
- 告警功能:支持根据预设的规则触发告警,及时通知相关人员。
- 示例:
- Prometheus 配置:
global: scrape_interval: 15s scrape_configs: - job_name: 'java_app' static_configs: - targets: ['localhost:8080']
- 告警规则:
groups: - name: example rules: - alert: HighCPUUsage expr: process_cpu_usage > 0.8 for: 5m labels: severity: critical annotations: summary: "High CPU usage on {{ $labels.instance }}" description: "CPU usage is above 80% (current value is: {{ $value }})"
- Prometheus 配置:
- Grafana
- 数据可视化:支持多种数据源的可视化,可以将 Prometheus 收集的指标数据进行可视化展示。
- 仪表盘定制:支持用户自定义仪表盘,可以根据需求定制个性化的监控仪表盘。
- 告警功能:支持告警功能,可以与 Prometheus 的告警规则结合,及时通知相关人员。
- 示例:
- Grafana 配置:在 Grafana 中创建数据源并导入 Prometheus 数据,创建仪表盘以实时监控 CPU、内存等指标。
- 告警规则:在 Grafana 中创建告警规则,当 CPU 使用率超过 80% 时触发告警。
- 其他系统监控工具
- Zabbix:开源的系统监控工具,支持多种监控功能,包括性能监控、告警、报表等。
- Nagios:开源的系统监控工具,支持多种监控功能,包括性能监控、告警、报表等。
- 示例:
- Zabbix 配置:
sudo apt-get install zabbix-agent
- Nagios 配置:
sudo apt-get install nagios-plugins
- Zabbix 配置:
(四)JVM 工具
- jstack
- 线程栈分析:用于获取 Java 应用程序的线程栈信息,分析线程状态和热点代码。
- 示例:
获取线程栈信息,分析是否存在死循环或热点代码。jstack -l <pid> > thread_dump.txt
- jmap
- 堆内存分析:用于导出 JVM 堆内存,结合工具(如 MAT、VisualVM)分析内存泄漏或对象分布情况。
- 示例:
导出 JVM 堆内存,使用工具(如 MAT)分析内存泄漏或对象分布情况。jmap -dump:format=b,file=heapdump.hprof <pid>
- jstat
- 垃圾回收分析:用于查看 JVM 的垃圾回收状态,分析垃圾回收的频率和耗时。
- 示例:
查看垃圾回收的状态,分析垃圾回收的频率和耗时。jstat -gc <pid> 1000
- jcmd
- JVM 操作:用于对 JVM 进行操作,如发送诊断命令、查看 JVM 的运行状态等。
- 示例:
查看 JVM 的运行状态。jcmd <pid> VM.info
(五)其他工具
- Arthas
- 实时诊断:支持实时诊断 Java 应用程序的运行状态,包括线程状态、内存使用情况、方法调用情况等。
- 命令行工具:提供丰富的命令行工具,方便用户快速排查问题。
- 可视化界面:支持可视化界面,方便用户查看诊断信息。
- 示例:
启动 Arthas,使用命令行工具进行实时诊断。java -jar arthas-boot.jar
查看线程状态。thread
查看可用处理器数量。ognl "@java.lang.Runtime@getRuntime().availableProcessors()"
四、问题排查流程
在 Java 线上问题排查过程中,遵循一定的流程可以提高排查效率,确保问题能够快速定位和解决。
(一)确定问题
- 问题描述
- 问题现象:详细描述问题的现象,如系统响应缓慢、用户报错、系统崩溃等。
- 问题频率:记录问题出现的频率,如是否是偶现问题、是否是周期性问题等。
- 问题范围:确定问题影响的范围,如是否影响所有用户、是否影响特定模块等。
- 示例:
- 问题描述:
问题现象:用户在访问 /api/user 接口时,响应时间超过 10 秒。 问题频率:每 10 分钟出现一次。 问题范围:仅影响 /api/user 接口。
- 问题描述:
- 问题分类
- 性能问题:根据问题现象,判断是否是性能问题,如响应时间过长、吞吐量不足等。
- 异常问题:根据问题现象,判断是否是异常问题,如运行时异常、业务逻辑异常等。
- 资源泄漏问题:根据问题现象,判断是否是资源泄漏问题,如内存泄漏、文件句柄泄漏等。
- 示例:
- 问题分类:
问题分类:性能问题(响应时间过长)
- 问题分类:
(二)收集信息
- 监控数据收集
- 系统性能监控数据:收集系统性能监控数据,如 CPU 使用率、内存使用率、磁盘 I/O、网络带宽等。
- 应用性能监控数据:收集应用性能监控数据,如响应时间、吞吐量、错误率等。
- 数据库性能监控数据:收集数据库性能监控数据,如查询响应时间、TPS、锁等待时间等。
- 示例:
- Prometheus 查询:
curl 'http://localhost:9090/api/v1/query?query=process_cpu_usage'
- Grafana 查询:在 Grafana 中查看 CPU 使用率的实时数据。
- Prometheus 查询:
- 日志收集
- 业务日志:收集业务日志,分析问题相关的日志记录。
- 系统日志:收集系统日志,分析系统运行状态和异常信息。
- 应用日志:收集应用日志,分析应用程序的运行状态和异常信息。
- 示例:
- 日志收集:
tail -f /var/log/app.log
- 日志分析:
grep "error" /var/log/app.log
- 日志收集:
- 其他信息收集
- 配置信息:收集系统配置信息、应用程序配置信息、数据库配置信息等,分析是否存在配置问题。
- 网络信息:收集网络配置信息、网络流量信息等,分析是否存在网络问题。
- 示例:
- 配置信息收集:
cat /etc/app/config.properties
- 网络信息收集:
netstat -an | grep :8080
- 配置信息收集:
(三)分析问题
- 业务日志分析
- 关键字搜索:通过关键字搜索业务日志,快速定位问题相关的日志记录。
- 日志关联分析:将不同模块或不同时间点的日志进行关联分析,找出问题的因果关系。
- 日志统计分析:对业务日志中的错误信息、性能指标等进行统计分析,找出问题的频率和分布规律。
- 示例:
- 关键字搜索:
grep "error" /var/log/app.log
- 日志关联分析:
grep "user_id=12345" /var/log/app.log
- 日志统计分析:
grep "error" /var/log/app.log | wc -l
- 关键字搜索:
- APM 分析
- 性能指标分析:通过 APM 工具查看应用程序的性能指标,分析是否存在性能问题。
- 链路追踪分析:通过 APM 工具查看请求的调用链路,分析是否存在性能瓶颈或异常调用。
- 分布式追踪分析:在分布式系统中,通过 APM 工具进行分布式追踪,分析服务之间的调用关系和性能瓶颈。
- 示例:
- 性能指标分析:
curl 'http://localhost:8080/metrics'
- 链路追踪分析:在 Skywalking 的 Dashboard 中查看某个请求的调用链路。
- 性能指标分析:
- 监控数据分析
- 系统性能指标分析:
- 示例:
- Prometheus 查询 CPU 使用率:
curl 'http://localhost:9090/api/v1/query?query=process_cpu_usage'
- Prometheus 查询内存使用率:
curl 'http://localhost:9090/api/v1/query?query=process_memory_usage'
- Grafana 查询:在 Grafana 中查看 CPU 和内存使用率的实时数据。
- Prometheus 查询 CPU 使用率:
- 示例:
- 应用性能指标分析:
- 示例:
- Prometheus 查询应用响应时间:
curl 'http://localhost:9090/api/v1/query?query=application_response_time'
- Prometheus 查询应用吞吐量:
curl 'http://localhost:9090/api/v1/query?query=application_throughput'
- Grafana 查询:在 Grafana 中查看应用响应时间和吞吐量的实时数据。
- Prometheus 查询应用响应时间:
- 示例:
- 数据库性能指标分析:
- 示例:
- Prometheus 查询数据库查询响应时间:
curl 'http://localhost:9090/api/v1/query?query=database_query_response_time'
- Prometheus 查询数据库 TPS:
curl 'http://localhost:9090/api/v1/query?query=database_tps'
- Grafana 查询:在 Grafana 中查看数据库查询响应时间和 TPS 的实时数据。
- Prometheus 查询数据库查询响应时间:
- 示例:
- 系统性能指标分析:
(四)解决问题
-
根据问题类型制定解决方案
- 性能问题解决方案:
- 优化代码逻辑:
- 示例:
public List<User> getAllUsers() { List<User> users = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { User user = new User(); user.setId(i); user.setName("User" + i); users.add(user); } return users; // 如果不需要全部用户,可以改为分页查询 }
- 示例:
- 优化数据库查询:
- 示例:
-- 原始查询 SELECT * FROM users WHERE age > 30; -- 优化后的查询 SELECT id, name FROM users WHERE age > 30 AND status = 'active';
- 示例:
- 优化线程池配置:
- 示例:
public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(20); // 增加线程池大小 for (int i = 0; i < 1000; i++) { executor.submit(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } executor.shutdown(); } }
- 示例:
- 优化代码逻辑:
- 异常问题解决方案:
- 修复代码逻辑错误:
- 示例:
public class NullPointerExample { public static void main(String[] args) { String str = "Hello"; // 修复 null 值 System.out.println(str.length()); } }
- 示例:
- 增加异常处理逻辑:
- 示例:
public class BusinessLogicExample { public static void main(String[] args) { int age = -1; if (age < 0) { throw new IllegalArgumentException("年龄不能为负数"); } System.out.println("年龄验证通过"); } }
- 示例:
- 修复代码逻辑错误:
- 资源泄漏问题解决方案:
- 修复内存泄漏:
- 示例:
public class MemoryLeakExample { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { while (true) { list.add(new Object()); if (list.size() > 1000) { // 限制列表大小 list.clear(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 示例:
- 修复文件句柄泄漏:
- 示例:
public class FileHandleLeakExample { public static void main(String[] args) { while (true) { try (FileInputStream fis = new FileInputStream("example.txt")) { fis.read(); } catch (IOException e) { e.printStackTrace(); } break; // 避免无限循环 } } }
- 示例:
- 修复数据库连接泄漏:
- 示例:
public class DatabaseConnectionLeakExample { public static void main(String[] args) { while (true) { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/example", "root", "password"); try { Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users"); while (rs.next()) { System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); // 确保关闭连接 } catch (SQLException e) { e.printStackTrace(); } } } break; // 避免无限循环 } } }
- 示例:
- 修复内存泄漏:
- 性能问题解决方案:
-
验证解决方案的有效性
- 验证性能问题解决方案:
- 示例:
- 验证优化后的代码逻辑:
通过压力测试工具(如 JMeter)验证优化后的代码逻辑是否有效。public List<User> getAllUsers() { List<User> users = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { User user = new User(); user.setId(i); user.setName("User" + i); users.add(user); } return users; // 如果不需要全部用户,可以改为分页查询 }
- 验证优化后的代码逻辑:
- 示例:
- 验证异常问题解决方案:
- 示例:
- 验证修复后的代码逻辑:
运行程序,确保不再抛出public class NullPointerExample { public static void main(String[] args) { String str = "Hello"; // 修复 null 值 System.out.println(str.length()); } }
NullPointerException
。
- 验证修复后的代码逻辑:
- 示例:
- 验证资源泄漏问题解决方案:
- 示例:
- 验证修复后的内存泄漏:
使用工具(如 VisualVM 或 MAT)监控内存使用情况,确保内存泄漏问题已修复。public class MemoryLeakExample { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { while (true) { list.add(new Object()); if (list.size() > 1000) { // 限制列表大小 list.clear(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 验证修复后的内存泄漏:
- 示例:
- 验证性能问题解决方案:
(五)验证效果
-
验证问题是否解决
- 验证性能问题是否解决:
- 示例:
- 验证优化后的代码逻辑:
通过压力测试工具(如 JMeter)验证优化后的代码逻辑是否有效。public List<User> getAllUsers() { List<User> users = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { User user = new User(); user.setId(i); user.setName("User" + i); users.add(user); } return users; // 如果不需要全部用户,可以改为分页查询 }
- 验证优化后的代码逻辑:
- 示例:
- 验证异常问题是否解决:
- 示例:
- 验证修复后的代码逻辑:
运行程序,确保不再抛出public class NullPointerExample { public static void main(String[] args) { String str = "Hello"; // 修复 null 值 System.out.println(str.length()); } }
NullPointerException
。
- 验证修复后的代码逻辑:
- 示例:
- 验证资源泄漏问题是否解决:
- 示例:
- 验证修复后的内存泄漏:
使用工具(如 VisualVM 或 MAT)监控内存使用情况,确保内存泄漏问题已修复。public class MemoryLeakExample { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { while (true) { list.add(new Object()); if (list.size() > 1000) { // 限制列表大小 list.clear(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 验证修复后的内存泄漏:
- 示例:
- 验证性能问题是否解决:
-
验证系统稳定性
- 验证系统是否恢复正常运行:
- 示例:
- 验证系统性能指标:
curl 'http://localhost:9090/api/v1/query?query=process_cpu_usage' curl 'http://localhost:9090/api/v1/query?query=process_memory_usage'
- 验证应用性能指标:
curl 'http://localhost:9090/api/v1/query?query=application_response_time' curl 'http://localhost:9090/api/v1/query?query=application_throughput'
- 验证数据库性能指标:
curl 'http://localhost:9090/api/v1/query?query=database_query_response_time' curl 'http://localhost:9090/api/v1/query?query=database_tps'
- 验证系统性能指标:
- 示例:
- 验证系统是否稳定运行:
- 示例:
- 监控系统日志:
tail -f /var/log/app.log
- 监控系统性能指标:
top free -m df -h netstat -an | grep :8080
- 监控系统日志:
- 示例:
- 验证系统是否恢复正常运行:
-
验证用户反馈
- 验证用户是否满意:
- 示例:
- 收集用户反馈:
echo "请用户反馈问题是否解决"
- 根据用户反馈调整解决方案:
echo "根据用户反馈调整解决方案"
- 收集用户反馈:
- 示例:
- 验证用户是否满意:
(六)持续优化
-
优化监控策略
- 优化监控指标:
- 示例:
- 增加监控指标:
global: scrape_interval: 15s scrape_configs: - job_name: 'java_app' static_configs: - targets: ['localhost:8080']
- 调整监控指标阈值:
groups: - name: example rules: - alert: HighCPUUsage expr: process_cpu_usage > 0.8 for: 5m labels: severity: critical annotations: summary: "High CPU usage on {{ $labels.instance }}" description: "CPU usage is above 80% (current value is: {{ $value }})"
- 增加监控指标:
- 示例:
- 优化告警机制:
- 示例:
- 增加告警规则:
groups: - name: example rules: - alert: HighMemoryUsage expr: process_memory_usage > 0.8 for: 5m labels: severity: critical annotations: summary: "High Memory usage on {{ $labels.instance }}" description: "Memory usage is above 80% (current value is: {{ $value }})"
- 调整告警通知方式:
global: resolve_timeout: 5m route: receiver: 'webhook' receivers: - name: 'webhook' webhook_configs: - url: 'http://localhost:9093/webhook'
- 增加告警规则:
- 示例:
- 优化监控指标:
-
改进告警机制
- 优化告警规则:
- 示例:
- 增加告警规则:
groups: - name: example rules: - alert: HighCPUUsage expr: process_cpu_usage > 0.8 for: 5m labels: severity: critical annotations: summary: "High CPU usage on {{ $labels.instance }}" description: "CPU usage is above 80% (current value is: {{ $value }})"
- 调整告警阈值:
groups: - name: example rules: - alert: HighMemoryUsage expr: process_memory_usage > 0.8 for: 5m labels: severity: critical annotations: summary: "High Memory usage on {{ $labels.instance }}" description: "Memory usage is above 80% (current value is: {{ $value }})"
- 增加告警规则:
- 示例:
- 优化告警通知方式:
- 示例:
- 增加告警通知方式:
global: resolve_timeout: 5m route: receiver: 'webhook' receivers: - name: 'webhook' webhook_configs: - url: 'http://localhost:9093/webhook'
- 增加告警通知方式:
- 示例:
- 优化告警规则:
-
升级分析工具
- 升级监控工具:
- 示例:
- 升级 Prometheus:
sudo apt-get install prometheus
- 升级 Grafana:
sudo apt-get install grafana
- 升级 Prometheus:
- 示例:
- 升级日志分析工具:
- 示例:
- 升级 ELK 堆栈:
sudo apt-get install elasticsearch logstash kibana
- 升级 Splunk:
sudo apt-get install splunk
- 升级 ELK 堆栈:
- 示例:
- 升级监控工具:
-
改进日志管理
- 优化日志记录策略:
- 示例:
- 优化 Log4j 配置:
<Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </Console> <File name="ApplicationLog" fileName="logs/app.log"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> <AppenderRef ref="ApplicationLog"/> </Root> </Loggers> </Configuration>
- 优化日志级别:根据需要调整日志级别,例如在生产环境中将日志级别设置为
INFO
或WARN
,以减少日志文件的大小。
- 优化 Log4j 配置:
- 示例:
- 优化日志存储策略:
- 示例:
- 配置日志轮转:
<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/app-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout> <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</Pattern> </PatternLayout> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile>
- 配置日志保留策略:设置日志文件的保留时间或数量,例如保留最近 30 天的日志文件。
- 配置日志轮转:
- 示例:
- 优化日志分析工具:
- 示例:
- 升级 ELK 堆栈:
sudo apt-get update sudo apt-get install elasticsearch logstash kibana
- 优化 Kibana 索引模式:根据日志数据的特点,优化 Kibana 的索引模式,提高查询效率。
PUT /_index_template/app-logs { "index_patterns": ["app-logs-*"], "settings": { "number_of_shards": 3, "number_of_replicas": 1 }, "mappings": { "properties": { "timestamp": { "type": "date" }, "level": { "type": "keyword" }, "message": { "type": "text" } } } }
- 升级 ELK 堆栈:
- 示例:
- 优化日志记录策略:
-
制定标准化流程
- 制定问题排查流程:
- 示例:
- 问题排查流程文档:
# Java 线上问题排查流程 ## 1. 确定问题 - 描述问题现象 - 记录问题频率 - 确定问题范围 ## 2. 收集信息 - 收集监控数据 - 收集日志信息 - 收集配置信息 ## 3. 分析问题 - 分析业务日志 - 分析 APM 数据 - 分析监控数据 ## 4. 解决问题 - 制定解决方案 - 验证解决方案 ## 5. 验证效果 - 验证问题是否解决 - 验证系统稳定性 - 收集用户反馈 ## 6. 持续优化 - 优化监控策略 - 改进告警机制 - 升级分析工具 - 改进日志管理
- 问题排查流程文档:
- 示例:
- 制定监控与告警标准:
- 示例:
- 监控与告警标准文档:
# Java 应用监控与告警标准 ## 1. 监控指标 - CPU 使用率 - 内存使用率 - 磁盘 I/O - 网络带宽 - 应用响应时间 - 应用吞吐量 - 数据库查询响应时间 - 数据库 TPS ## 2. 告警规则 - CPU 使用率 > 80% 持续 5 分钟 - 内存使用率 > 80% 持续 5 分钟 - 应用响应时间 > 2 秒 持续 5 分钟 - 数据库查询响应时间 > 1 秒 持续 5 分钟 ## 3. 告警通知方式 - 邮件通知 - 短信通知 - Webhook 通知 ## 4. 监控工具配置 - Prometheus 配置 - Grafana 配置 - ELK 堆栈配置
- 监控与告警标准文档:
- 示例:
- 制定日志管理标准:
- 示例:
- 日志管理标准文档:
# Java 应用日志管理标准 ## 1. 日志记录策略 - 日志级别:INFO、WARN、ERROR - 日志格式:`%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n` - 日志存储位置:`/var/log/app.log` ## 2. 日志存储策略 - 日志轮转:每天轮转一次,保留最近 30 天的日志 - 日志压缩:使用 gzip 压缩旧日志文件 ## 3. 日志分析工具 - ELK 堆栈 - Splunk - Graylog ## 4. 日志分析流程 - 关键字搜索 - 日志关联分析 - 日志统计分析
- 日志管理标准文档:
- 示例:
- 制定问题排查流程:
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 想让你多爱自己一些的开源计时器
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析