Java内存分析常用命令

jps

JVM Process Status Tool

  • -l : 输出主类全名或 jar 路径
  • -q : 只输出 LVMID
  • -m : 输出 JVM 启动时传递给 main()的参数
  • -v : 输出 JVM 启动时显示指定的 JVM 参数

举个栗子:

[root@ubuntu /data/web/logs 14:05:57]$ jps -lmv
19813 /data/web/web-1.0.jar -Xms256m -Xmx256m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+HeapDumpOnOutOfMemoryError -Dspring.profiles.active=staging -XX:+DisableExplicitGC -Dfile.encoding=UTF-8 -Djava.awt.headless=true

jmap

JVM Memory Map

生成dump文件

jmap -dump:format=b,file=dump.hprof 666

666 是进程号

生成的 hprof 文件直接使用 jvisualvm.exe 打开就可以了

查看 java 堆使用情况

jmap -heap ${pid}

在 GC 开始的时候,对象只会存在于 Eden 区和名为“From”的 Survivor 区,Survivor 区“To”是空的。紧接着进行 GC,Eden 区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold 来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次 GC 后,Eden 区和 From 区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次 GC 前的“From”,新的“From”就是上次 GC 前的“To”。不管怎样,都会保证名为 To 的 Survivor 区域是空的。Minor GC 会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gH36qW0A-1640103936796)(E:\coder\img\java\image-20210601162040188.png)]

可能遇到:ERROR: ptrace(PTRACE_ATTACH, ..) failed for 78347: Operation not permitted问题。这是因为一些调试软件利用 ptrace 来进行获取某进程的内存状态的(包括GDB), ptrace-scope为了防止用户访问当前正在运行的进程的内存和状态 , 默认情况下不允许再访问了。解决方式:

# 获取权限
sudo su root
 
# 临时修改
echo 0 > /proc/sys/kernel/yama/ptrace_scope # 原值是1

# 永久修改
sudo vim /etc/sysctl.d/10-ptrace.conf
kernel.yama.ptrace_scope = 0

jinfo

查看 java 的参数信息:VM flags、Java system properties

使用:jinfo

Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

jstack

查看进程的线程信息,当前的调用堆栈

查找CPU 过高线程

定位当前 cpu 使用较大的线程:

  1. top -Hp ${pid} 查看进程中线程的 cpu 占用情况
  2. printf "0x%x\n" ${nid} 把线程 id 转化成 16 进制
  3. jstack ${pid} | grep -A20 ${nid} nid 是线程的 id,需要转化为 16 进制

jstat

查看堆内存各部分的使用量

jstat -gc ${pic} 2s

查看 gc 统计,2s 更新一次

  • S0C:第一个幸存区的大小
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • EC:伊甸园区的大小
  • EU:伊甸园区的使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间
  • NGCMN:年轻代(young)中初始化(最小)的大小(字节)
  • NGCMX:年轻代(young)的最大容量 (字节)
  • NGC:年轻代(young)中当前的容量 (字节)
  • OGCMN:old 代中初始化(最小)的大小 (字节)
  • OGCMX:old 代的最大容量(字节)
  • OGC:old 代当前新生成的容量 (字节)
  • OC:Old 代的容量 (字节)
  • PGCMN:perm 代中初始化(最小)的大小 (字节)
  • PGCMX:perm 代的最大容量 (字节)
  • PGC:perm 代当前新生成的容量 (字节)
  • PC:Perm(持久代)的容量 (字节)
  • linux 中通过 man jstat 可以查看到所有的 option 和每个字段的含义
  • 1.7 版本之后,Perm 改成了 Metaspace,永久代。从 jdk1.8 开始,自带的 hostspot 虚拟机取消了过去的永久区,而新增了 metaspace 区,从功能上看,metaspace 可以认为和永久区类似,其最主要的功用也是存放类元数据,但实际的机制则有较大的不同。 首先,metaspace 默认的最大值是整个机器的物理内存大小,所以 metaspace 不断扩张会导致 java 程序侵占系统可用内存,最终系统没有可用的内存;而永久区则有固定的默认大小,不会扩张到整个机器的可用内存。当分配的内存耗尽时,两者均会触发 full gc,但不同的是永久区在 full gc 时,以堆内存回收时类似的机制去回收永久区中的类元数据(Class 对象),只要是根引用无法到达的对象就可以回收掉,而 metaspace 判断类元数据是否可以回收,是根据加载这些类元数据的 Classloader 是否可以回收来判断的,只要 Classloader 不能回收,通过其加载的类元数据就不会被回收。这也就解释了我们这两个服务为什么在升级到 1.8 之后才出现问题,因为在之前的 jdk 版本中,虽然每次调用 fastjson 都创建了很多代理类,在永久区中加载类很多代理类的 Class 实例,但这些 Class 实例都是在方法调用是创建的,调用完成之后就不可达了,因此永久区内存满了触发 full gc 时,都会被回收掉。 而使用 1.8 时,因为这些代理类都是通过主线程的 Classloader 加载的,这个 Classloader 在程序运行的过程中永远也不会被回收,因此通过其加载的这些代理类也永远不会被回收,这就导致 metaspace 不断扩张,最终耗尽机器的内存了。

jcmd

使用help可以列出当前所有可用命令
jcmd ${pid} help

命令使用方式:
jcmd ${pid} ${cmd}

常见的几个命令:

  • 查看虚拟机启动时间 VM.uptime
  • 打印线程栈信息 Thread.print
  • 查看系统中类统计信息 GC.class_histogram
  • 导出堆信息 GC.heap_dump, 这个命令功能和 jmap -dump 功能一样
  • 获取系统 Properties 内容 VM.system_properties
  • 获取启动参数 VM.flags

堆外内存查看

直接查看当前情况

jcmd $pid VM.native_memory detail

堆外内存查看首先需要添加 java 启动参数:-XX:NativeMemoryTracking=detail,重启项目后使用来查看情况

查看变化

  1. 通过-XX:NativeMemoryTracking设置了summary或者detail级别的track参数
  2. 当程序启动之后执行jcmd ${pid} VM.native_memory baseline设定基准
  3. 一段时间之后jcmd ${pid} VM.native_memory summary.diff 或者 jcmd $pid VM.native_memory detail.diff生成前后变化情况

参考:java文档NMT内存分类

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值