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
相关推荐
被漂一组20 小时前
在线解决window和linux对linux远程问题
linux·运维·服务器
森G20 小时前
2六Ubuntu文件系统移植
linux·ubuntu
dot to one21 小时前
Centos 7 环境下mysql的安装及配置
linux·mysql·centos
stillaliveQEJ21 小时前
【JVM】基础概念之为什么要使用JVM
jvm
「QT(C++)开发工程师」21 小时前
C++语言编程规范-风格
linux·开发语言·c++·qt
-指短琴长-21 小时前
CGroups资源控制实战【Linux】
linux·运维·服务器
昵称什么的不存在1 天前
WPeChatGPT 插件使用教程(转载)
linux·逆向
码农hbk1 天前
xv6 源码精读(二)开启MMU、一致性映射页表
linux·服务器
雪碧聊技术1 天前
Linux命令过关挑战
linux·运维·数据库
liulilittle1 天前
在 Android Shell 终端上直接运行 OPENPPP2 网关路由配置指南
android·linux·开发语言·网络·c++·编程语言·通信