这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
一、前言
因为我的是mac电脑,所以运行程序都是在mac上,有时一些工具在mac上不是很好用。如果有不好用的情况,可以参考文章:
以上是我在mac上运行Jmap时遇到的问题,如果你也遇到了,可以查看。
二、Jmap使用
- Jmap -histo 进程号
这个命令是用来查看系统内存使用情况的,实例个数,以及占用内存。
命令:
1 | yaml复制代码jmap -histo 3241 |
运行结果:
1 | yaml复制代码 |
这里显示的是,byte类型的数组,有多少个实例,占用多大内存。
- num:序号
- instances:实例数量
- bytes:占用空间大小
- class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int
- Jmap -heap 进程号
注意:Jmap命令在mac不太好用,具体参考前言部分。
windows或者linux上运行的命令是
1 | 复制代码Jmap -heap 进程号 |
mac上运行的命令是:(jdk8不能正常运行,jdk9以上可以)
1 | css复制代码jhsdb jmap --heap --pid 2139 |
执行结果
1 | ini复制代码Attaching to process ID 2139, please wait... |
通过上述结果分析,我们查询的内容如下:
- 进程号:2139
- JDK版本号:11
- 使用的垃圾收集器:G1(jdk11默认的)
- G1垃圾收集器线程数:8
- 还可以知道堆空间大小,已用大小,元数据空间大小等等。
- 新生代,老年代region的大小。容量,已用,空闲等。
- Jmap -dump 导出堆信息
这个命令是导出堆信息,当我们线上有内存溢出的情况的时候,可以使用Jmap -dump导出堆内存信息。然后再导入可视化工具用jvisualvm进行分析。
导出命令
1 | ini复制代码jmap -dump:file=a.dump 进程号 |
我们还可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
1 | ruby复制代码1. -XX:+HeapDumpOnOutOfMemoryError |
下面有案例说明如何使用。
三、jvisualvm命令工具的使用
- 基础用法
上面我们有导出dump堆信息到文件中,可以使用jvisualvm工具导入dump堆信息,进行分析。
打开jvisualvm工具命令:
1 | 复制代码jvisualvm |
打开工具界面如下:
点击文件->装入,可以导入文件,查看系统的运行情况了。
2.案例分析 - 堆空间溢出问题定位
下面通过工具来分析内存溢出的原因。
第一步:自定义一段可能会内存溢出的代码,如下:
1 | java复制代码import com.aaa.jvm.User; |
第二步:配置参数
为了方便看到效果,所以我们会设置两组参数。
第一组:设置堆空间大小,将堆空间设置的小一些,可以更快查看内存溢出的效果
1 | ruby复制代码 ‐Xms10M ‐Xmx10M ‐XX:+PrintGCDetails |
设置的堆内存空间是10M,并且打印GC
第二组:设置内存溢出自动导出dump****文件(内存很大的时候,可能会导不出来)
1 | ruby复制代码1. -XX:+HeapDumpOnOutOfMemoryError |
将这两组参数添加到项目启动配置中。
运行的过程中打印堆空间信息到文件中:
1 | lua复制代码jmap -dump:file=a.dump,format=b 12152 |
后面我们可以使用工具导入堆文件进行分析(下面有说到)。
我们还可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
完整参数配置如下:
1 | ruby复制代码-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/zhangsan/Downloads |
这里需要注意的是堆目录要写绝对路径,不能写相对路径。
第三步:启动项目,等待内存溢出
我们看到,运行没有多长时间就内存溢出了。
查看导出到文件的目录:
第四步:导入堆内存文件到jvisualvm工具
文件->装入->选择刚刚导出的文件
第五步:分析
我们主要看【类】这个模块。
通过上图我们可以明确看出,有三个类实例数特别多,分别是:byte[],java.lang.String,com.lxl.jvm.User。前两个我们不容易看出是哪里的问题,但是第三个类com.lxl.jvm.User我们就看出来了,问题出在哪里。接下来就重点排查调用了这个类的地方,有没有出现内存没有释放的情况。
这个程序很简单,那么byte[]和java.lang.String到底是什么呢?我们的User对象结构中字段类型是String。
1 | arduino复制代码public class User { |
既然有很多User,自然String也少不了。
那么byte[]是怎么回事呢?其实String类中有byte[]成员变量。所以也会有很多byte[]对象。
本文转载自: 掘金