主要是三种 Event:
当进入同步块,尝试获取锁的时候,产生 JavaMonitorEnter Event;当调用 Object.wait() 进入等待时,会产生 JavaMonitorWait Event;当 锁升级(另一种说法是锁膨胀)时,产生 JavaMonitorWait Event。
下面我从网上看到的这张图,有助于理解这三种事件:
一般的在默认情况下, JavaMonitorEnter 和 JavaMonitorWait 采集到的在阈值以上的会比较多,一般不会有 JavaMonitorInflated 事件,除非发生 CPU 资源耗尽或者程序不断 dump 导致一直处于 safepoint。
但是并不是所有的 JavaMonitorEnter 和 JavaMonitorWait Event 都是我们关心的,如何快速找到我们关心的关键 Event 呢?
先举一个 JavaMonitorEnter 的例子: 从事件浏览器视角去看, Event 太多了,我们建一个 JavaMonitorEnter Event 的视图:
一般的,我会按照 Monitor class 去分类看,争用同一个对象锁一般是同一个业务:
我们来看第一个计数最多的,点击这个分类,在下面的列表按照持续需时间倒序,查看线程以及堆栈:
发现是因为本地缓存更新,导致比较慢,这里本地缓存是读取的数据库,读取的数据比较多,400ms 的比较正常。
我们再来看另一个,Monitor class 为 java.lang.Object 的:
从堆栈上看出是获取 Lettuce 连接时候,锁等待了320ms。查看源代码,发现是连接初始化,导致比较慢,初始化好连接之后没再出现了。
这里建议,针对微服务应用,再调高阈值到 50ms。
四个事件:
这些事件我们一般都不关心,Java 线程阻塞与热点方法和 CPU 消耗等,有其他的 Event,在 default.jfc 中这四个 Event 默认都是采集的,这里建议关闭这四个 Event 的采集
堆栈采集对于这种 Event 很重要,但是对于 Spring Cloud 这样的框架,调用层次极为复杂,可能默认采集堆栈深度(64)不够,需要增大才能看到自己的业务代码堆栈。但是要注意的一点是:堆栈采集深度,对于性能影响很大,以最坏的情况考虑,可以理解为增加多少倍的堆栈深度,对性能的影响就提高多少倍。 建议对于常态化的线上监控,堆栈深度最多不超过 128.
JVM 启动参数包含很多配置, 同时也可以通过 JVMTI,jcmd 命令等等动态修改这些配置, 如果我们想看这些配置以及修改的时间点,那么可以打开这些 Event 的采集:
+``-
配置的哪些状态位,例如-XX:+UseCompressedOops
就是打开压缩对象指针-XX:InitialRAMPercentage=52.0
配置初始内存堆栈占用比例(只有在没指定-Xmx
和-Xms
的时候有效)这个对性能影响是很小的,所以在系统自带的 default.jfc 中就打开了。我这里建议还是打开,毕竟基本所有状态位是可以通过 jcmd 命令修改的,如果有对比需求,对比修改前还有修改后性能影响,那么状态位变换时间就很重要了
主要包括三种 Event:
这些事件我们平常开发一般不会去关心,一般之后开发框架或者定位框架问题的时候,才会去关心类加载器相关的问题。而且这个用阿里开源的工具 Arthas
更加好用(https://alibaba.github.io/arthas/sc.html):
$ sc -d demo.MathGame
class-info demo.MathGame
code-source /private/tmp/arthas-demo.jar
name demo.MathGame
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name MathGame
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@3d4eac69
+-sun.misc.Launcher$ExtClassLoader@66350f69
classLoaderHash 3d4eac69
Affect(row-cnt:1) cost in 875 ms.