159 lines
3.7 KiB
Markdown
159 lines
3.7 KiB
Markdown
|
|
# Linux 排查Java进程内存过高完整方法
|
|||
|
|
|
|||
|
|
# Linux 排查 Java 进程内存过高完整方法
|
|||
|
|
|
|||
|
|
## 一、先找到占用高内存的 Java 进程
|
|||
|
|
|
|||
|
|
### 方式 1:top(最常用,实时查看)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
top
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 按 `M` 大写 M:**按内存占用降序排序**
|
|||
|
|
|
|||
|
|
- 找到 `COMMAND` 列是 `java` 的行,记下 PID(第一列数字)
|
|||
|
|
|
|||
|
|
### 方式 2:htop(更友好,需要安装)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
htop
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方式 3:ps 一次性列出所有 java 进程,看 RSS \(物理内存\)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
ps -ef | grep java
|
|||
|
|
# 带内存占用版本
|
|||
|
|
ps aux | grep java
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 字段说明:`%MEM` 内存占比,`RSS` 实际物理内存(单位 KB)
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
### 方式 4:只看 java 进程,排序内存
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
ps aux | grep -E 'java' | grep -v grep | sort -k4 -nr
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、拿到 PID 后,查看 JVM 内部内存情况(JDK 自带工具)
|
|||
|
|
|
|||
|
|
> ⚠️ 前提:运行 java 的用户和执行 jps/jstat/jmap 的用户一致,否则会权限失败
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
### 1\. jps 快速列出所有 Java 进程 \+ PID
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
jps -l
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`-l`:打印完整主类名,方便区分是哪个 Java 应用
|
|||
|
|
|
|||
|
|
### 2\. jstat 持续监控 JVM 堆内存变化(判断是否内存泄漏)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 每1秒输出一次,持续观察,PID替换成你的java进程号
|
|||
|
|
jstat -gc <PID> 1000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
重点看这几列:
|
|||
|
|
|
|||
|
|
- `S0C,S1C`:Survivor 区容量
|
|||
|
|
|
|||
|
|
- `EC,OC`:Eden、Old 老年代容量
|
|||
|
|
|
|||
|
|
- `OU`:Old 区已使用大小
|
|||
|
|
|
|||
|
|
> 如果 **OU 持续上涨,Full GC 频繁但内存释放很少 → 大概率内存泄漏**
|
|||
|
|
>
|
|||
|
|
>
|
|||
|
|
|
|||
|
|
### 3\. jmap 查看堆内存快照(dump 内存文件,事后分析)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 生成堆dump文件(会短暂STW,生产环境谨慎操作)
|
|||
|
|
jmap -dump:format=b,file=heapdump.hprof <PID>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
得到 `heapdump.hprof` 文件,可以下载到本地,用 **MAT\(Memory Analyzer Tool\)** 分析,定位是哪个对象占用大量内存。
|
|||
|
|
|
|||
|
|
查看堆内存概况(不用生成文件):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
jmap -heap <PID>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
输出 JVM 堆配置:新生代、老年代大小,GC 收集器类型。
|
|||
|
|
|
|||
|
|
查看对象统计:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
jmap -histo <PID>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
列出所有类实例数量、占用内存,找出占用最多的类。
|
|||
|
|
|
|||
|
|
### 4\. jstack 查看线程栈(排查是否线程堆积间接导致内存暴涨)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
jstack <PID>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
用来查找死锁、大量等待线程。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、查看进程级详细内存(Linux 系统层面,区分 JVM 堆 / 非堆)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看进程smaps,详细内存分布
|
|||
|
|
cat /proc/<PID>/smaps
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
或者用 `pmap`
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pmap -x <PID>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
可以看到:堆内存、栈、共享库占用。
|
|||
|
|
|
|||
|
|
## 四、快速排查思路总结
|
|||
|
|
|
|||
|
|
1. `top` / `ps aux` → 确认是 Java 进程占用高,拿到 PID
|
|||
|
|
|
|||
|
|
2. `jstat -gc PID 1000` → 观察 GC 行为,判断是内存泄漏还是堆配置太小
|
|||
|
|
|
|||
|
|
3. `jmap -heap PID` → 看 JVM 堆参数是否合理(Xmx/Xms)
|
|||
|
|
|
|||
|
|
4. `jmap -dump` 导出堆快照,MAT 分析定位泄漏对象
|
|||
|
|
|
|||
|
|
5. `jstack` 辅助排查线程问题
|
|||
|
|
|
|||
|
|
## 五、常见原因
|
|||
|
|
|
|||
|
|
1. **内存泄漏**:代码创建对象没有释放(集合持续 add 不清理)
|
|||
|
|
|
|||
|
|
2. JVM 参数不合理:`-Xmx` 设置太大,吃掉大量宿主机内存
|
|||
|
|
|
|||
|
|
3. 堆外内存(DirectBuffer)占用:jmap 看不到堆外内存,需要额外排查
|
|||
|
|
|
|||
|
|
4. 并发请求过高,正常业务内存开销
|
|||
|
|
|
|||
|
|
## 六、小提示
|
|||
|
|
|
|||
|
|
- 如果是容器环境(docker),`top`看到的是容器内视图,宿主机需要额外查看
|
|||
|
|
|
|||
|
|
- dump 堆文件会占用磁盘空间,hprof 文件体积可能很大
|
|||
|
|
|
|||
|
|
- 如果 JDK 是精简版(没有自带 jmap/jstat),需要安装完整 JDK 或者使用`arthas`(阿里开源 Java 诊断工具)
|
|||
|
|
|
|||
|
|
如果你需要,我可以给你一份 **Arthas 一键排查命令集**,不需要依赖 JDK 自带工具。
|
|||
|
|
|
|||
|
|
> (注:部分内容由豆包工作 AI 生成)
|