3/18日晚生产环境nifi突然告警,Prometheus metrics无法访问,且系统cpu和内存飙升。

    连上环境先tail日志,发现ExecuteGroovyProcessor执行报错:java.lang.OutOfMemoryError: Compressed class space,导dump后重启,问题暂时解决。第二天查到Compressed class space是元空间中的Klass空间被占满的问题导致的,直觉加载Groovy脚本可能会导致该问题,遂一直根据dump追查ExecuteGroovyProcessor可能出现的内存泄露问题,而ExecuteGroovyProcessor加载脚本是缓存的,没发现问题。

    后来将日志拖到第一次报这个错误的地方,发现最初的报错是我们项目自己实现的Processor报的错。于是很快就定位到了动态数据库连接缓存不可能命中的代码bug。因为缓存无法命中,所以一直创建新的连接,每次创建连接时都会调用URLClassLoader去加载驱动jar包。

    排查问题的过程中了解了jcmd命令,发现jcmd命令可以查看classloader的状态,于是就使用了一下命令:

    jcmd pid VM.metaspace
    命令可以打印Non-class space 和 Class space 大小。

    jcmd pid VM.classloader_stats
    命令可以打印每个ClassLoader加载的类数量以及占用元空间的大小。

    可以发现每个ClassLoader加载的空间总和时接近metaspace的总大小的。

    统计结果是URLClassLoader占用了差不多90%得元空间,确认是创建连接导致的。