运维市场在2020年前景分析
「可以看到堆内存中,有一些nio有关的大对象,比如正在接收消息队列消息的nioChannel,还有nio.HeapByteBuffer,但是数量不多,不能作为判断的依据,先放着观察下。」 下一步,我开始浏览该接口代码,接口内部主要逻辑是调用集团的WCS客户端,将数据库表中数据查表后写入WCS,没有其他额外逻辑 发觉没有什么特殊逻辑后,我开始怀疑WCS客户端封装是否存在内存泄漏,这样怀疑的理由是,WCS客户端底层是由SCF客户端封装的,作为RPC框架,其底层通讯传输协议有可能会申请直接内存。 「是不是我的代码出发了WCS客户端的Bug,导致不断地申请直接内存的调用,最终吃满内存。」 我联系上了WCS的值班人,将我们遇到的问题和他们描述了一下,他们回复我们,会在他们本地执行下写入操作的压测,看看能不能复现我们的问题。 既然等待他们的反馈还需要时间,我们就准备先自己琢磨下原因。 「我将怀疑的目光停留在了直接内存上,怀疑是由于接口调用量过大,客户端对nio使用不当,导致使用ByteBuffer申请过多的直接内存。」 「画外音:最终的结果证明,这一个先入为主的思路导致排查过程走了弯路。在问题的排查过程中,用合理的猜测来缩小排查范围是可以的,但最好先把每种可能性都列清楚,在发现自己深入某个可能性无果时,要及时回头仔细审视其他可能性。」 沙箱环境复现 为了能还原当时的故障场景,我在沙箱环境申请了一台压测机器,来确保和线上环境一致。 「首先我们先模拟内存溢出的情况(大量调用接口):」 我们让脚本继续推送数据,调用我们的接口,我们持续观察内存占用。
当开始调用后,内存便开始持续增长,并且看起来没有被限制住(没有因为限制触发Full GC)。 「这里需要额外注意的是:永久代(JDK8的原生去)存放JVM运行时使用的类,永久代的对象在full GC时进行垃圾收集。」 复习完了JVM的内存分配,让我们回到故障上来。 堆内存分析 虽说一开始就基本确认与堆内存无关,因为泄露的内存占用超过了堆内存限制4G,但是我们为了保险起见先看下堆内存有什么线索。 我们观察了新生代和老年代内存占用曲线以及回收次数统计,和往常一样没有大问题,我们接着在事故现场的容器上dump了一份JVM堆内存的日志。 堆内存Dump
堆内存快照dump命令: 可以看到案发当时调用量相比正常情况(每分钟200+次)提高了很多(每分钟5000+次)。 「我们暂时让脚本停止发送消息,该接口调用量下降到每分钟200+次,容器内存不再以极高斜率上升,一切似乎恢复了正常。」 接下来排查这个接口是不是发生了内存泄漏。 排查过程 首先我们先回顾下Java进程的内存分配,方便我们下面排查思路的阐述。 「以我们线上使用的JDK1.8版本为例」。JVM内存分配网上有许多总结,我就不再进行二次创作。 JVM内存区域的划分为两块:堆区和非堆区。
(编辑:淮安站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |