没有打印日志时,排查生产问题,怎么办?

有时候,系统出了bug,某些变量取值不符合预期,而我们又没有打印日志,如何能快速知道运行时这些变量的值到底是什么呢?这就需要用到dump对快照了,dump堆快照能保存dump堆的那一时刻内存中的变量,通过堆快照分析工具MAT,查找缓存的值 。
下面使我们一个真实生产案例:
我们计费平台本地缓存了历史免费调用量,计费时发现计费金额有问题,通过分析,怀疑是免费调用量计算错误,免费调用量会缓存到本地内存,因为没有打印日志,所以想知道,缓存中的值到底是什么 。
我们本地缓存使用的的guava缓存,key为一个对象类 :com...vo.cache.
这个对象有三个成员变量:
java.time.// 查询日期
java.lang.// 产品编码
java.lang.// 机构编码
我们现在想知道缓存中机构为“”, 产品为“”的历史免费调用量,按照以下步骤进行查找 。
【没有打印日志时,排查生产问题,怎么办?】1.dump堆快照文件
先找到java进程PID,然后使用jdk自带的jmap命令dump堆:
jmap -dump:live,format=b,file=${文件路径} ${PID}
注意:
1.执行jmap命令的用户要与 要dump堆文件java进程的启动用户一致 。
2. jamp命令的jdk版本要与启动java进程的jdk版本一致 。
2. MAT导入堆快照文件并分析
此处操作省略...,不清楚MAT如何导入堆文件的,自己百度,非常简单 。
3. 编写OQL语句,查找缓存key对象

没有打印日志时,排查生产问题,怎么办?

文章插图
OQL语句不知道是什么的同学自己百度,基本用法非常简单 。
OQL类似于SQL,是jvm的对象查询语言,能够查询堆快照中存在的任意实例化的对象,分析工具MAT就是基于OQL的 。
根据已知的缓存key对象的属性去查找缓存key对象,OQL语句如下:
SELECT * FROM com.fintell.charge.vo.cache.ProductCacheKeyVo pck WHERE pck.productCode.toString().equals("HYXJ_P") AND pck.institutionCode.toString().equals("hanyixiaojin")
然后在MAT中执行此OQL语句,如下图所示:
查询到4行记录,每一行代表一个 类实例化的对象,说明有4个类的实例化对象,点击某一行记录,即可在左侧选项卡中查看到其成员变量的名称和取值列表,如下图所示:
左侧可以看到它的三个成员变量的取值 。
再点击右侧对象列表中对象左侧折叠按钮或双击对象,即可展开对象列出其成员变量,点击某个成员变量,即可在左侧选项卡中查看到其成员变量的成员变量名称和列表(这句话有点绕,好好理解一下),如下图所示:
根据业务实现逻辑,可以知道我们计费系统有两类缓存用到了这个对象,一类是产品的计费策略缓存,一类是历史免费调用量缓存,经过分析,可以判定前三个对象是属于2021年04月17日、18日和19日这三天的计费策略,最后一个对象就是我们要找的历史免费调用量的缓存对象 。
重点来了,到底是如何分析的呢?这就需要进行引用关系分析了 。
4. 引用关系分析
引用关系分析就是分析的实例对象是被谁引用了和的实例对象引用了谁,我们已经通过OQL查询到了的实例对象列表,所以我们现在要找谁引用了这些实例对象作为缓存的key,从而找到缓存对象,再找到对应的缓存的值 。在MTA工具中提供了with和with,可以找到实例对象的引用关系 。
右键点击第一个对象,选择List,如下图所示:
没有打印日志时,排查生产问题,怎么办?

文章插图
List 菜单下有两个选项:
1. with
代表当前对象直接引用的对象的集合