JVM常用工具:jstat、jmap、jstack

内存溢出

小结

jps找出进程号,jmap转储heap文件,jhat分析堆文件。抑或可以jvisual等直接监控分析堆。

模拟代码

java 复制代码
// 内存泄漏示例:静态集合持有对象,无法被 GC 回收
public class OutMemoryDemo {
    // 静态集合:生命周期与 JVM 一致,对象无法被回收
    private static final List<byte[]> LEAK_LIST = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始内存泄漏...");
        int count = 0;
        while (true) {
            // 每次分配 1MB 内存(byte[1024 * 1024])
            LEAK_LIST.add(new byte[1024 * 1024]);
            count++;
            // 每 100 次打印一次内存占用
            if (count % 100 == 0) {
                System.out.printf("已分配 %d 个对象,总占用约 %d MB%n",
                        count, count * 1);
            }
            // 短暂休眠,减缓内存增长速度(方便观察)
            Thread.sleep(50);
        }
    }
}

转储堆文件

cmd 复制代码
jmap -dump:live,format=b,file=F:/Temp/heap_dump.hprof  28204

分析堆文件

cmd 复制代码
jhat F:/Temp/heap_dump.hprof

由于1.8的bin下有jhat:访问localhost:7000

PS:这里的class[B指的是Java的基础变量byte,对应的C是char,这个大家自然不陌生可以推断出其他的变量。

垃圾回收

Jstat

jstat(Java Virtual Machine Statistics Monitoring Tool) 是 JDK 自带的命令行工具,用于监控 JVM 运行时状态,包括类加载、GC、堆内存、编译等统计信息。

在IDEAL中的VM OPTIONS中加入(Run Configure):

复制代码
-Xms64m -Xmx64m -XX:+PrintGCDetails JstatDemo

测试代码

事实上,可以采用直接设置List对象进行反复加入和清空来模拟取代设置堆内存大小启动参数和GC日志打印

java 复制代码
public class JstatDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始测试 jstat...");
        int count=0;

        // 无限循环分配小对象(触发 GC)
        while (true) {
            // 分配 1MB 内存(byte 数组)
            byte[] data = new byte[5 * 1024 * 1024];
            // byte[] data = new byte[100 * 1024];
            System.out.println("已经分配内存:" + ++count + "0MB");
            // System.out.println("已经分配内存:" + ++count + "00KB");
            // 短暂休眠(让 jstat 能捕捉到内存变化)
            Thread.sleep(2000);
        }
    }
}

分析GC

jstat [option] pid [interval] [count]

复制代码
jstat -gcutil 26072 1000 3

为什么是Old区呢,因为对象太大,将分配对象修改为100KB

补充参数常用选项:

OPTIONS

选项 说明
-class 监控类加载/卸载数量及耗时(Loaded/Unloaded/Time)。
-compiler 显示 JIT 编译器编译的方法数量及耗时。
-gc 堆内存各区域容量(Capacity)及实际使用量(Usage),包含 GC 次数/时间。
-gccapacity 堆内存各区域的容量(最小/最大/当前)。
-gcutil 以百分比显示堆内存使用率,并输出 GC 统计。
-gccause -gcutil,额外显示最近一次和当前 GC 的原因(如 Allocation Failure)。
-gcnew 新生代(Young Gen)内存统计。
-gcold 老年代(Old Gen)内存统计。
-gcmetacapacity 元空间(Metaspace)容量统计(JDK8+)。
-gc 输出字段
字段 含义 示例
S0C Survivor 0 区容量(KB) 10240.0
S1C Survivor 1 区容量 10240.0
S0U Survivor 0 区已使用量 0.0
S1U Survivor 1 区已使用量 1024.0
EC Eden 区容量 81920.0
EU Eden 区已使用量 40960.0
OC 老年代容量 204800.0
OU 老年代已使用量 102400.0
YGC Young GC 次数 100
YGCT Young GC 总耗时(秒) 5.456
FGC Full GC 次数 3
FGCT Full GC 总耗时 1.234
GCT GC 总耗时 6.690
-gcutil 输出字段(百分比形式)
字段 含义 示例
S0 Survivor 0 区使用率 50.00
S1 Survivor 1 区使用率 0.00
E Eden 区使用率 75.25
O 老年代使用率 85.30
M 元空间(Metaspace)使用率 95.80
CCS 压缩类空间(Compressed Class Space)使用率 80.50
YGC Young GC 次数 200
YGCT Young GC 总耗时 10.20
FGC Full GC 次数 5
FGCT Full GC 总耗时 3.50
GCT 总 GC 耗时 13.70

Jinfo

jinfo对应进程PID:

cmd 复制代码
jinfo [pid]

注意:使用jdk指令的时候,往往要注意对应java程序的版本尽可能的保持一致,否则可能出现:

Error attaching to process: Windbg Error: GetModuleParameters failed!

我这里采用1.8的jinfo分析jdk17运行程序的进程pid出现如上报错

可视化监控Jconsole/Jvisualvm

一般可视化的集成了常用的指令(但是服务器和环境不一定会让我们使用或连接得上,工作多年的同志们可以理解),方便展示图形(相对于堆的64MB),修改分配内存为5MB:

死锁

这一块儿包括上面,其实21年就已经有记录,不过没有总结归纳(就像有很多存货和系列其实没发布整理),就不重新再跑和复现了(可视化对应的应该是jvisualvm):

cmd 复制代码
jstack -heap pid
相关推荐
专注代码七年4 小时前
IDEA JVM优化配置idea64.vmoptions - 保守兼容版本 兼容IDEA 2023.3.6版本【亲测可用】
java·jvm·intellij-idea
こ进制掌控者4 小时前
Ubuntu server 24.04.3 设置静态IP
linux·tcp/ip·ubuntu
泡沫冰@4 小时前
shell编程:sed - 流编辑器(5)
linux
花心蝴蝶.5 小时前
JVM 内存结构
java·开发语言·jvm
xiaguangbo5 小时前
rust slint android 安卓
android·linux·rust
晓梦初醒p5 小时前
finalshell 连接服务器报错channel is not opened
linux·运维·服务器
准时准点睡觉6 小时前
HTTP 错误 403.14 - Forbidden Web 服务器被配置为不列出此目录的内容——错误代码:0x00000000
运维·服务器·iis·asp.net
cpsvps6 小时前
环境变量管理于美国服务器多环境部署的实施标准
运维·服务器·数据库
IvorySQL6 小时前
【生态再升级】IvorySQL 4.5 与银河麒麟高级服务器操作系统V11完成适配认证!
运维·服务器·数据库·postgresql·ivorysql
conkl6 小时前
在ARM64 Ubuntu 20.04上部署Mailu邮件系统:深度排查Docker Bridge网络通信失败问题
linux·ubuntu·docker