原因: 下午先是收到钉钉告警有一个消费者系统任务积压, 当时以为就是有范围上量没有当回事,后来客服群开始反馈说有客户的数据没有生成。这个时候查看mq的后台,发现任务堆积数量还是很多。 这个时候登录一台消费者系统查看日志,发现OOM
[2022-07-09 16:40:34.640] [ERROR] [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-9] [org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.run:1251]
- Consumer thread error, thread abort. java.lang.OutOfMemoryError: Java heap space at java.lang.String.<init>(String.java:662) at com.alibaba.fastjson.serializer.SerializeWriter.toString(SerializeWriter.java:503) at com.alibaba.fastjson.JSON.toJSONString(JSON.java:760) at com.alibaba.fastjson.JSON.toJSONString(JSON.java:696) at com.alibaba.fastjson.JSON.toJSONString(JSON.java:661) at sun.reflect.GeneratedMethodAccessor169.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120) at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:50) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:211) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:143) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:132) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1569) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1488) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer$$Lambda$976.0000000000000000.invokeListener(Unknown Source) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1476) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1467) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1411) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:958) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:908) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:81) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1279) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1185) at java.lang.Thread.run(Thread.java:819)
先看第一个重点:Consumer thread error, thread abort.
从错误中就可以明显看出来,问题的原因:是因为消费线程在执行的过程中,出现内存溢出,导致线程和MQ断开链接。最终导致消息堆积的出现,只要解决内存溢出的问题即可。
OOM的问题排查
线程OOM后打印的文件我们用MAT工具排查(公司使用的JDK是openJ9)
从图像上很明显看出那里占用的大部分内存, 有一个超级大的map,继续分析看看map里的内容是谁。
根据类路径去代码中查询为什么会生成这么多对象。
首先找到在那里使用了这个类
这是logback的类,当时使用这个做日志的打印是想打印这个数据的业务流转日志。 单独输出到一个文件中,方便问题的排查。
知道这个类以后再深入查看一下map数据怎么来的
从这里看到使用了final的map并且在调用write的时候获取helper是调用的computeIfAbsent。
computeIfAbsent()
1、首先会判断map中是否有对应的Key;
2.如果没有对应的Key,则会创建一个满足Value类型的数据结构放到Value的位置中;
3.如果有对应的Key,则会操作该Key对应的Value.
在这里就怀疑是不是因为我程序中每次传递的mapper都是新new的。然后就找我调用的方法。
这个类是我用来logback扩展json字段转换用的。 我在构造JsonGenerator的时候每次都是新new ObjectMapper(), 所以会导致map每次比较的时候发现传递的值是不相同的,会new 一个新的放到Map中。最终导致map变的异常大。
修改很简单