大纲:
命令简介 --> 命令与生产实践间的关联
1. 命令简介:
jps:当前所有java进程pid,
jstat:用于查询JVM内存使用情况和GC次数及耗时;
参数列表含义
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)S1C:年轻代中第二个survivor(幸存区)的容量 (字节)S0U :年轻代中第一个survivor(幸存区)目前已使用空间 (字节)S1U :年轻代中第二个survivor(幸存区)目前已使用空间 (字节)EC :年轻代中Eden(伊甸园)的容量 (字节)EU :年轻代中Eden(伊甸园)目前已使用空间 (字节)OC :Old代的容量 (字节)OU :Old代目前已使用空间 (字节)MC:metaspace(元空间)的容量 (字节)MU:metaspace(元空间)目前已使用空间 (字节)YGC :从应用程序启动到采样时年轻代中gc次数YGCT :从应用程序启动到采样时年轻代中gc所用时间(s)FGC :从应用程序启动到采样时old代(全gc)gc次数FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s)GCT:从应用程序启动到采样时gc用的总时间(s)
- jstat -gc pid :打印pid的java进程的gc详情
- jstat -gc pid 1000 10 :每隔1秒打印一次gc详情,打印10次
jmap:它可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列
我常用其打印dumo文件,用作MAT分析
jmap -dump:format=b,file=heapdump.phrof pid
jstack:查询线程情况
2. 命令与生产实践间的关联
如何感知内存增长速率,及常见的fullGC及内存分配问题
首先根据前面总结的知识点,我们可以通过jstat -gc pid,来了解到以下信息:
- 了解GC内存分配情况,分配的是否合理有一个最初的感知;
- 通过GC时长和GC的次数,计算出每次GC的耗时,这样我们就知道了FullGC的触发时间频率,及GC的平均时长;
- 通过观察最近一段时间的gc情况,可以看到内存数据的增长情况,对数据的增加速率有一定的了解;
- 通过每次GC之后,数据是迁移到survivor区还是迁移到老年代有一个清晰认知;
- 每次gc之后,对存活对象的大小有感知,方便合理的设置内存
由大对象引发的问题
首先先说一下大对象引入的几种场景:
- 由于sql不合理,导致将全表数据都查询出来;
- 由于要将大量数据加载到内存中,进行计算;
由于 重试机制 或 超时时间 引发的问题
假如对方系统出现问题,由于超时时间过长,或 重试机制设置不合理,会导致我们的系统大量的请求积压进来,长时间不能释放对象,从而导致频繁的gc,这就需要我们通过jmap dump文件,来查看大量存活的对象是哪些?查看其堆栈信息,定位到代码位置,对代码进行分析测试定位。
由于反射或-XX:SoftRefLRUPolicyMSPerMB参数设置不合理引发的问题
首先,这个问题不常见,也不建议设置此参数,关于这块的内存,详见此篇文章
System.gc()引发的问题
因为System.gc()会触发fullGC,所以建议系统内不要写System.gc()
CPU负载过高常见的场景
- 系统创建了大量的线程,并行运行,而且工作负载都很重。
- FullGC是非常耗费cpu资源的,如果频繁触发fullGC,会导致cpu负载过高
频繁fullGC的场景
- 内存分配不合理,导致对象频繁进入老年代;
- 存在内存泄漏等问题,导致大量对象塞满了老年代;
- 永久代里的类太多,触发了fullGC
NIO DirectByteBuffer导致的堆外内存溢出
由于大量DirectByteBuffer对象因某种原因,进入了老年代,DirectByteBuffer对象长时间没有被回收,导致堆外内存也无法被回收,最终导致内存溢出