disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
      • CPU 占满
      • 内存泄漏
      • 死锁
      • 线程频繁切换
      • 接口变慢了应该如何排查
      • 频繁 full gc
      • OOM
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2022-06-03
Java
目录

java 的常见性能问题分析以及出现场景

一般来说 java 出现性能问题就是代码写的不怎么样,需要使用一些命令或者工具手动定位到问题代码并进行优化

# CPU 占满

这种问题一般出现在使用了大量的计算资源,比如:

GTA5 联机版的要跑19.8亿次 if 语句、堪称游戏开发史上最大的“屎山”代码,一支烟的功夫游戏都加载不完。存在了7年,R 星从没想过要修复。这大量的 if 判断需要消耗 CPU 资源,导致 CPU 占满

那么如何定位到代码问题所在呢?

可以使用 top 命令,top 命令使我们最常用的 Linux 命令之一,它可以实时的显示当前正在执行的进程的 CPU 使用率,内存使用率等系统信息。top -Hp pid 可以查看线程的系统资源使用情况 在这里插入图片描述

定位到线程之后可以使用 jstack(用于生成当前时刻的线程快照。线程快照是当前 Java 虚拟机内存中每一条线程正在执行的方法堆栈的集合。生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题)来动态监视 jvm 的栈使用情况,jstack 输出的内容如下: 在这里插入图片描述 出现 CPU 占满问题,可能是线程竞争导致了频繁的上下文切换,或者代码中存在无限循环或者条件判断错误导致的死循环

# 内存泄漏

程序中存在无用但是存在引用的对象

常见的情况有:忘记断开引用,使用生命周期很长的对象,ThreadLocal 使用不当

使用 JProfiler 来分析及定位内存泄漏问题

也可以使用 linux 的命令来定位问题,比如 top 和 vmstat

vmstat 是一个指定周期和采集次数的虚拟内存检测工具,可以统计内存,CPU,swap 的使用情况,它还有一个重要的常用功能,用来观察进程的上下文切换

确认是 java 的内存占用过多后(业务平台应该有报警),我们可以使用 jstat(JVM统计监测工具)来看堆中每个区域占用了多大空间,下图中 EC 表示新生代大小,EU 表示使用量,OC 表示老年代大小,其他同理 在这里插入图片描述

我们使用 jmap 来看堆中对象的存活情况,我们可以比较哪个对象过多或者过大导致内存泄漏 在这里插入图片描述 我们还可以用 jmap 把进程内存使用情况 dump 到文件中,再用 jhat 分析查看 在这里插入图片描述

请注意,堆 dump 是将 JVM 堆的完整快照写入磁盘文件的过程,因此需要 STW(Stop-The-World 事件),即暂停所有应用线程,等待 dump 完成。这会导致应用暂时不可用,因此在生产环境中应该谨慎使用

同时 JVM 也提供了很多优化措施,比如我们可以在 jmap 时只生成直方图提升速度、使用并行线程加速 dump(JDK 11+特性)、使用异步 profiler 等等

# 死锁

死锁会导致耗尽线程资源,占用内存,表现就是内存占用升高,如果是直接 new 线程,会导致 JVM 内存被耗尽,报无法创建线程的错误,这也是体现了使用线程池的好处

死锁的出现必须满足四大条件,互斥条件、请求与保持条件、不可剥夺条件、循环等待条件,预防死锁只要避免其中一个条件就可以了,如按序获取资源、一次申请所有要使用的资源、申请不到资源时,可以放弃已有资源

死锁可以用 jstack 排查

# 线程频繁切换

上下文切换会导致将大量 CPU 时间浪费在寄存器、内核栈以及虚拟内存的保存和恢复上,导致系统整体性能下降。当你发现系统的性能出现明显的下降时候,需要考虑是否发生了大量的线程上下文切换

通过 top 命令以及 top -Hp pid 查看进程和线程CPU情况,发现 Java 线程 CPU 占满了,但是线程CPU使用情况很平均,没有某一个线程把 CPU 吃满的情况,这种情况就是线程频繁切换的典型例子

# 接口变慢了应该如何排查

以上所有问题都会导致接口变慢,所以接口变慢是一个很大很宽泛的问题,为了排查这个问题,我们需要考虑各种情况:

如果是单个接口变慢,可能是该接口涉及到的某个组件出现了问题,我们可以对涉及到的每个方法进行记录,记录每个方法的执行时长,定位到最终问题

如果是多个接口变慢,可能是项目整体出现了问题,涉及到的每个组件都可能是问题源,我们需要先确定范围:

客户端 → 网络 → 负载均衡 → 应用服务器 → 中间件 → 数据库/缓存 → 外部服务
1

每个范围应该都有对应的报警,如果可以确定是外部报警,就能直接结束排查。如果是中间件、外部服务这些,可以告知下游,我们还可以做一些降级处理

我们需要解决的,是应用服务器、数据库出现慢查这两个范围的问题

如果是服务器问题,可以使用 top、ps、vmstat、jstat 等命令看状态,确认是否 CPU 占满(死循环)、CPU 负载高(使用了不合规的线程池)、内存占满(内存泄漏、频繁生成大对象)、频繁 full gc(堆大小设置太小)等问题

这其中有分两类问题,一是需要调整系统才可以处理的问题(堆大小设置太小),这类问题比较少见,如果出现不建议更改除了堆大小以外的 jvm 参数,建议扩容

第二类是代码问题,我们可以通过线程 ID 找到调用栈,更改对应业务代码处理问题,比如死循环;可以通过 jstat 拉 dump 文件查看内存泄漏、频繁生成大对象等问题

# 频繁 full gc

频繁 full gc 一般会进行报警,我们可以用 jstat 查看 gc 情况

## 1. jstat - 实时GC统计
jstat -gc <pid> 1000 10  ## 每秒1次,共10次
## 关键指标:
## FGC/FGCT:Full GC次数/耗时
## GCT:总GC时间

## 2. jstat -gcutil - 百分比形式
jstat -gcutil <pid> 1000

## 3. 监控输出示例
S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
0.00  94.44  33.27  87.36  95.77  91.70   2174   65.825    15    9.234   75.059
## ↑ 关注:FGC频率、FGCT时长
1
2
3
4
5
6
7
8
9
10
11
12
13

同时,在一个系统设计之初,就需要开启 gc 日志,以便发生 gc 问题的时候,快速定位问题

## JVM启动参数
java -Xms2g -Xmx2g \
-XX:+PrintGCDetails \              ## 打印GC详情
-XX:+PrintGCDateStamps \           ## 打印时间戳
-XX:+PrintHeapAtGC \               ## GC前后堆信息
-XX:+PrintTenuringDistribution \   ## 年龄分布
-XX:+PrintGCApplicationStoppedTime \ ## STW时间
-XX:+PrintPromotionFailure \       ## 晋升失败
-Xloggc:/path/to/gc.log \          ## GC日志文件
-XX:+UseGCLogFileRotation \        ## 日志轮转
-XX:NumberOfGCLogFiles=10 \        ## 保留10个文件
-XX:GCLogFileSize=50M \            ## 每个文件50M
-jar your-app.jar

## 或者使用统一的日志框架
-XX:+PrintGC \
-XX:+PrintGCTimeStamps \
-XX:+PrintHeapAtGC \
-XX:+PrintGCApplicationStoppedTime \
-XX:+PrintGCApplicationConcurrentTime \
-XX:+PrintTenuringDistribution \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=10M \
-Xloggc:/var/log/myapp/gc-%t.log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

这些 gc 日志可以直接看,也可以使用一些图形化分析工具来看,gc 日志一般长下面这样

## GC日志示例分析
2024-01-15T14:23:45.123+0800: 345.678: [Full GC (Ergonomics) 
  [PSYoungGen: 102400K->0K(204800K)] 
  [ParOldGen: 409600K->387654K(512000K)] 512000K->387654K(716800K)
, [Metaspace: 65432K->65432K(1060864K)], 1.234567 secs]
[Times: user=3.45 sys=0.12, real=1.23 secs]

## 关键信息:
1. Full GC原因:Ergonomics(自适应调整)
2. 年轻代:102400K → 0K(全部回收)
3. 老年代:409600K → 387654K(回收不多)
4. 元空间:无变化
5. 耗时:1.23秒(STW时间)
1
2
3
4
5
6
7
8
9
10
11
12
13

看到以上信息后,可以使用 dump 文件,分析堆中内容,看看是否有内存泄漏、本地缓存使用过多的情况

# OOM

oom 一般有以下几种情况:

// 1. Heap Space OOM(最常见)
java.lang.OutOfMemoryError: Java heap space

// 2. GC Overhead Limit Exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded

// 3. Metaspace OOM(元空间)
java.lang.OutOfMemoryError: Metaspace

// 4. Direct Buffer Memory(直接内存)
java.lang.OutOfMemoryError: Direct buffer memory

// 5. Unable to create new native thread
java.lang.OutOfMemoryError: unable to create new native thread

// 6. Requested array size exceeds VM limit
java.lang.OutOfMemoryError: Requested array size exceeds VM limit

// 7. Out of swap space
java.lang.OutOfMemoryError: Out of swap space

// 8. Kill process or sacrifice child
java.lang.OutOfMemoryError: Kill process or sacrifice child
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

重点是在启动项目时要额外设置参数,不然拿不到错误时的 dump 文件,也就没办法分析了。此外还需要开启 gc 日志

## OOM时自动dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/java_heapdump_%p_%t.hprof
1
2
3

当 JVM 发生 OOM(OutOfMemoryError)时,JVM 进程本身通常不会直接宕机,但容器可能会被杀掉

JVM 内部的 OOM,比如堆内存 OOM(最常见),抛出 OutOfMemoryError,但JVM 进程本身不会自动退出。后续的内存分配尝试可能会失败,不会直接宕机,但应用程序通常处于不可用状态。理论上可以通过 try-catch 捕获并尝试恢复,但实际上很难,因为堆已满,很难执行有意义的恢复操作

// 典型的堆内存溢出
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
1
2

OOM Killer 是 Linux 内核在内存耗尽时的紧急制动系统。它通过一个基于内存使用量和可调优先级的评分机制来选择牺牲品。对于系统管理员和开发者,理解其原理并合理设置关键进程的 oom_score_adj,或使用更先进的 cgroups 进行内存隔离,是保证系统稳定性的重要手段

我之前遇到过日志打的太多导致 OOM 了,因为大量日志导致堆外内存溢出

#Java
最后更新: 2/23/2026, 9:23:04 AM
Java 的 Excel 相关操作
java 基础知识

← Java 的 Excel 相关操作 java 基础知识→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式