Java系统的监控经常是通过JMX的,最近几年也比较流行使用Dropwizard和SpringMetrics的集成。

Jvm自身有一个叫PerfData的指标集,是使用mmap来存储的,jstat就是基于它的。

我们平时在生产环境中使用jstat来观察JVM的GC情况(时间、次数、分代大小),

但是PerfData还有很多其他指标,而且非常有用。

下面就来介绍一下PerfData。

基本概念

Perfdata里大概有200-300个指标,有数值类型的,也有字符串的(比如gc cause)。

你可以使用MonitoredVm.findByPattern(““)来获取全部指标的列表,也可以用名字获取单个指标。

JVM运行时,不断在内存中更新指标值,通过mmap映射到/tmp/hsperfdata_user/pid的文件中。

这个文件的大小是32K,mmap文件默认是30s才刷新同步,perfdata的文件延迟是特殊控制的。

如果你使用过mmap就会知道有个force接口,可以强制同步更新,perfdata的更新频率在100ms以内。

提一个题外话,jvm启动后会生成一个/tmp/.javapidxxxxx的文件,这个文件是一个socket,

常用的jstack、jmap、jcmd都是通过他来和jvm通讯的,不要和perfdata的文件弄混淆了。

获取指标的方法

因为有一个独立的mmap文件存在,所以想要获取指标并不需要和jvm进行通讯,

只要你的程序有权限读取这个文件即可,当然读取这个文件最好使用java提供的类库,方便一些。

如果你想自己解析,可以参考这个类的实现,sun.jvmstat.perfdata.monitor.v2_0.PerfDataBuffer。

特别注意一下其中的ByteOrder,字节存储机制主要有两种:Big-endian和Little-endian。

相关细节就不展开讲了,自行查阅资料吧。

Arguments JPS_ARGUMENTS = new Arguments(new String[] {"-l"});
MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(JPS_ARGUMENTS.hostId());
MonitoredVm vm = this.monitoredHost.getMonitoredVm(new VmIdentifier("//" + pid + "?mode=r"), 1000);

这段代码你可以在jstat的源码中找到,根据需要自行修改。

指标

这几百个指标我们也并非都关心,有很多指标的含义都搞不清楚,需要读读JVM的源码才能知道。

今年年初我们在开发基于Perfdata的JVM性能指标监控时,进行了一些整理工作,主要采集了三五十个指标。

具体指标的含义今后会更新,这里只列一下大致的分类。

1、TLAB:

  • 1.1、sun.gc.tlab.alloc
  • 1.2、sun.gc.tlab.allocThreads
  • 1.3、sun.gc.tlab.fills
  • 1.4、sun.gc.tlab.gcWaste

2、Threshold:

  • 2.1、sun.gc.policy.tenuringThreshold
  • 2.2、sun.gc.policy.maxTenuringThreshold
  • 2.3、sun.gc.policy.incrementTenuringThresholdForGcCost
  • 2.4、sun.gc.policy.decrementTenuringThresholdForGcCost
  • 2.5、sun.gc.policy.decrementTenuringThresholdForSurvivorLimit

3、Thread:

  • 3.1、java.threads.live
  • 3.2、java.threads.started

4、Safepoint:

  • 4.1、sun.rt.safepoints
  • 4.2、sun.rt.safepointTime
  • 4.3、sun.rt.safepointSyncTime

5、Memory:

  • 5.1、sun.gc.generation.0.space.X.used
  • 5.2、sun.gc.generation.0.space.X.capacity

6、Lock:

  • 6.1、sun.rt._sync_ContendedLockAttempts
  • 6.2、sun.rt._sync_Parks
  • 6.3、sun.rt._sync_Notifications
  • 6.4、sun.rt._sync_FutileWakeups
  • 6.5、sun.rt._sync_Inflations
  • 6.6、sun.rt._sync_Deflations

7、GC:

  • 7.1、sun.gc.collector.X.invocations
  • 7.2、sun.gc.collector.X.time
  • 7.3、sun.gc.policy.avgMinorPauseTime(avgMajorPauseTime)
  • 7.4、sun.gc.policy.avgPromotedAvg
  • 7.5、sun.gc.policy.avgSurvivedAvg
  • 7.6、sun.gc.policy.gcTimeLimitExceeded
  • 7.7、sun.gc.policy.survivorOverflowed

8、Compile:

  • 8.1、java.ci.totalTime
  • 8.2、sun.ci.totalCompiles
  • 8.3、sun.ci.osrTime
  • 8.4、sun.ci.osrCompiles
  • 8.5、sun.ci.totalBailouts
  • 8.6、sun.ci.totalInvalidates

9、Class:

  • 9.1、sun.cls.time
  • 9.2、java.cls.loadedClasses
  • 9.3、java.cls.unloadedClasses

10、AgeTable:

  • 10.1、sun.gc.generation.0.agetable.bytes.XX

选取的这些指标主要是为了监控性能和性能调优。

除了指标类型的监控,也可以尝试使用一下jitwatch和jfr,都会有一些意想不到的收获。

Flags

Jvm在启动时,可以配置很多参数,这个参数也是有API可以获取的。

VirtualMachine vm = VirtualMachine.attach(pid);
HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm;
InputStream in = hvm.executeJCmd("VM.flags -all");

这个代码你可以从Jcmd的源码中找到,网上能搜到VM.flags,很少有人提到-all这个参数。

看看你的Jvm到底都有哪些flag,你又知道多少flag的含义呢。