大纲
1.使用jstat了解线上系统的JVM运行状况
2.使用jmap和jhat了解线上系统的对象分布
3.如何分析JVM运行状况并合理优化
4.使用jstat分析模拟的BI系统JVM运行情况
5.使用jstat分析模拟的计算系统JVM运行情况
6.问题汇总
1.使用jstat了解线上系统的JVM运行状况
(1)JVM的整体运行原理简单总结
(2)功能强大的jstat
(3)jstat -gc PID
(4)其他的jstat命令
(5)到底该如何使用jstat工具
(6)新生代对象增长的速率
(7)Young GC的触发频率和每次耗时
(8)每次Young GC后有多少对象进入老年代
(9)Full GC的触发时机和耗时
(1)JVM的整体运行原理简单总结
一.对象优先在Eden区分配
二.Young GC的触发时机和执行过程
三.对象进入老年代的时机
四.Full GC的触发时机和执行过程
接下来介绍如何使用工具分析运行的系统:
一.对象增长的速率
二.Young GC的触发频率
三.Young GC的耗时
四.每次Young GC后有多少对象存活下来
五.每次Young GC后有多少对象进入老年代
六.老年代对象增长的速率
七.Full GC的触发频率
八.Full GC的耗时
(2)功能强大的jstat
如果平时要对运行中的系统,检查其JVM的整体运行情况。比较实用的工具之一就是jstat,它可以轻易让我们看到当前JVM内:Eden区、S区、老年代的内存情况,以及YGC和FGC的执行次数和耗时。
通过这些指标,我们就可以轻松分析出当前系统的运行情况。从而判断当前系统的内存使用压力和GC压力,以及内存分配是否合理。
(3)jstat -gc PID
首先使用jps命令在生产机器Linux上,找出Java进程的PID。接着就针对我们的Java进程执行:jstat -gc PID,这样就可以看到这个Java进程的内存和GC情况了。运行这个命令后会看到如下指标的信息:
这些指标都是非常实用的JVM GC分析指标。
(4)其他的jstat命令
除了上面的jstat -gc命令是最常用的以外,jstat还有一些命令可以看到更多详细的信息,如下所示:
但最完整、最常用、最实用的还是jstat -gc命令,jstat -gc命令基本足够分析JVM的运行情况了。
(5)到底该如何使用jstat工具
首先需要明确,分析线上的JVM进程,最想要知道的信息有哪些?最想要知道的信息会包括如下:
一.新生代对象增长的速率
二.Young GC的触发频率
三.Young GC的耗时
四.每次Young GC后有多少对象存活下来
五.每次Young GC后有多少对象进入老年代
六.老年代对象增长的速率
七.Full GC的触发频率
八.Full GC的耗时
只要知道了这些信息,我们就可以结合前面介绍的JVM GC优化的方法:合理分配内存,尽量让对象留在年轻代不进入老年代,避免频繁FGC。这就是对JVM最好的性能优化了。
(6)新生代对象增长的速率
需要了解JVM的第一个信息就是:随着系统运行,每秒会在新生代的Eden区分配多少对象。
要获取该信息,只需要在Linux机器上运行命令:jstat -gc PID 1000 10,该命令意思:每隔1秒更新最新jstat统计信息,一共执行10次jstat统计。通过这行命令,可以灵活地对线上机器,通过固定频率输出统计信息。从而观察出每隔一段时间,JVM中Eden区的对象占用变化。
比如执行这行命令后:第一秒显示出Eden区使用了200M内存,第二秒显示出Eden区使用了205M内存,第三秒显示出Eden区使用了209M内存,以此类推。此时我们就可以推断出来,这个系统大概每秒钟会新增5M左右的对象。
而且这里可以根据自己系统的情况灵活多变地使用,如果系统负载很低,则不一定每秒进行统计,可以每分或每10分来统计,以此查看系统每隔1分钟或者10分钟大概增长多少对象。
此外,系统一般有高峰和日常两种状态,比如系统高峰期用户很多,我们应该在系统高峰期去用上述命令看看高峰期的对象增长速率,然后再在非高峰的日常时间段内看看对象的增长速率,这样就可以了解清楚系统的高峰和日常时间段内的对象增长速率了。
(7)Young GC的触发频率和每次耗时
接着需要了解JVM的第二个信息是:大概多久会触发一次YGC,以及每次YGC的耗时。其实多久触发一次YGC是很容易推测出来的,因为系统高峰和日常时的对象增长速率都知道了,根据对象增长速率和Eden区大小,就可以推测出:高峰期多久发生一次YGC,日常期多久发生一次YGC。
比如Eden区有800M内存:如果发现高峰期每秒新增5M对象,那么大概3分钟会触发一次YGC。如果发现日常期每秒新增0.5M对象,那么大概半小时才触发一次YGC。
那么每次Young GC的平均耗时呢?jstat会展示迄今为止系统已经发生了多少次YGC以及这些YGC的总耗时。比如系统运行24小时后共发生260次YGC,总耗时为20s。那么平均每次YGC大概耗时几十毫秒,我们由此可以大概知道每次YGC时会导致系统停顿几十毫秒。
(8)每次Young GC后有多少对象进入老年代
接着要了解JVM的第三个信息是:每次YGC后有多少存活对象,即有多少对象会进入老年代。
其实每次YGC过后有多少对象会存活下来,只能大致推测出来。假设已经推算出高峰期多久会发生一次YGC,比如3分钟会有一次YGC。那么此时就可以执行下述jstat命令:jstat -gc PID 180000 10。这就相当于让JVM每隔三分钟执行一次统计,连续执行10次。观察每隔三分钟发生一次YGC时,Eden、Survivor、老年代的对象变化。
正常来说:Eden区肯定会在几乎放满后又变得很少对象,比如800M只使用几十M。Survivor区肯定会放入一些存活对象,老年代可能会增长一些对象占用。所以这时观察的关键,就是观察老年代的对象增长速率。
正常来说:老年代不太可能不停快速增长的,因为普通系统没那么多长期存活对象。如果每次YGC后,老年代对象都要增长几十M,则可能存活对象太多了。存活对象太多可能会导致放入S区后触发动态年龄判定规则进入老年代,存活对象太多也可能导致S区放不下,大部分存活对象需要进入老年代。
如果老年代每次在YGC过后就新增几百K或几M的对象,这个还算正常。但如果老年代对象快速过快增长,那一定是不正常的。
所以通过上述观察策略,就可以知道每次YGC后有多少对象是存活的,也就是Survivor区里增长的 + 老年代增长的对象,就是存活的对象。
通过jstat -gc也可以知道老年代对象的增长速率,比如每隔3分钟一次YGC,每次会有50M对象进入老年代,于是老年代对象的增长速率就是每隔3分钟增长50M。
(9)Full GC的触发时机和耗时
只要知道老年代对象的增长速率,那么Full GC的触发时机就很清晰了。比如老年代有800M,每3分钟新增50M,则每1小时就会触发一次FGC。
根据jstat输出的系统运行迄今为止的FGC次数以及总耗时,就能计算出每次FGC耗时。比如一共执行了10次FGC,共耗时30s,那么每次FGC大概耗费3s左右。
2.使用jmap和jhat了解线上系统的对象分布
(1)jstat总结
(2)使用jmap了解系统运行时的内存区域
(3)使用jmap了解系统运行时的对象分布
(4)使用jmap生成堆内存转储快照
(5)使用jhat在浏览器中分析堆转出快照
(1)jstat总结
通过jstat可以非常轻松便捷的了解到线上系统的运行状况,如新生代对象和老年代对象增速、YGC和FGC触发频率以及耗时等,通过jstat可以完全了解线上系统的JVM运行情况,为优化做准备。
接下来介绍两个在工作中非常实用的工具:jmap和jhat。这两个工具可以观察线上JVM中的对象分布,能更加细致了解系统运行。即了解系统运行过程中,哪些对象占据了大部分,占据了多少内存空间。
(2)使用jmap了解系统运行时的内存区域
其实如果只是了解JVM运行状况,然后进行GC优化,通常jstat就够用了。但有时会发现JVM新增对象速度很快,想知道什么对象占那么多内存。从而可以优化对象在代码中的创建时机,避免对象占用内存过大。
首先看一个命令:jmap -heap PID。这个命令可以打印出一系列信息,这些信息大概就是:堆内存相关的一些参数设置、当前堆内存里各个区域的情况。比如:Eden区的总容量、已经使用的容量、剩余的空间容量,两个Survivor区的总容量、已经使用的容量、剩余的空间容量,老年代的总容量、已经使用的容量、剩余的容量。
但是这些信息其实jstat已经有了,所以一般不会用jmap去获取这些信息。毕竟jmap的这种信息还没jstat全面,比如jmap就没有GC相关的统计。
(3)使用jmap了解系统运行时的对象分布
jmap中比较有用的一个命令是:jmap -histo PID。这个命令会打印出类似下面的信息:按各对象占用内存空间大小降序排列,把占用内存最多的对象放在最上面。