You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kafka Akka Actor抛出Java堆空间错误:Pod内存未耗尽的原因排查

OpenShift上Akka+Kafka应用偶发Java堆空间错误排查

我在OpenShift上运行一个基于Akka的Kafka应用,该应用会偶尔抛出Java堆空间错误:

Uncaught error from thread [transparency_16_1_a_ecp_aggregator-akka.actor.default-dispatcher-4]: Java heap space, shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[transparency_16_1_a_ecp_aggregator]

java.lang.OutOfMemoryError: Java heap space at java.base/java.nio.HeapByteBuffer.(Unknown Source) at java.base/java.nio.ByteBuffer.allocate(Unknown Source) at org.apache.kafka.common.memory.MemoryPool$1.tryAllocate(MemoryPool.java:30) at org.apache.kafka.common.network.NetworkReceive.readFrom(NetworkReceive.java:113) at org.apache.kafka.common.network.KafkaChannel.receive(KafkaChannel.java:452) at org.apache.kafka.common.network.KafkaChannel.read(KafkaChannel.java:402) at org.apache.kafka.common.network.Selector.attemptRead(Selector.java:674) at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:576) at org.apache.kafka.common.network.Selector.poll(Selector.java:481) at org.apache.kafka.clients.NetworkClient.poll(NetworkClient.java:560) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.poll(ConsumerNetworkClient.java:265) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.poll(ConsumerNetworkClient.java:236) at org.apache.kafka.clients.consumer.KafkaConsumer.pollForFetches(KafkaConsumer.java:1306) at org.apache.kafka.clients.consumer.KafkaConsumer.poll(KafkaConsumer.java:1242) at org.apache.kafka.clients.consumer.KafkaConsumer.poll(KafkaConsumer.java:1215) at KafkaGeneratingUnitsRepository.getGeneratingUnits(KafkaGeneratingUnitsRepository.java:84) at .GeneratingUnitsConsumerActor.consumeGeneratingUnits(GeneratingUnitsConsumerActor.java:65) at actor.GeneratingUnitsConsumerActor$$Lambda$633/0x00000001005d2440.apply(Unknown Source) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:24) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:20) at scala.PartialFunction.applyOrElse(PartialFunction.scala:214) at scala.PartialFunction.applyOrElse$(PartialFunction.scala:213) at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:20) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:269) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:270) at akka.actor.Actor.aroundReceive(Actor.scala:537) at akka.actor.Actor.aroundReceive$(Actor.scala:535) at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:220) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580) at akka.actor.ActorCell.invoke(ActorCell.scala:548) at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270) at akka.dispatch.Mailbox.run(Mailbox.scala:231)

进而引发以下错误:

java.lang.OutOfMemoryError: Java heap space at java.base/java.nio.HeapByteBuffer.(Unknown Source) at java.base/java.nio.ByteBuffer.allocate(Unknown Source) at org.apache.kafka.common.memory.MemoryPool$1.tryAllocate(MemoryPool.java:30) at org.apache.kafka.common.network.NetworkReceive.readFrom(NetworkReceive.java:113) at org.apache.kafka.common.network.KafkaChannel.receive(KafkaChannel.java:452) at org.apache.kafka.common.network.KafkaChannel.read(KafkaChannel.java:402) at org.apache.kafka.common.network.Selector.attemptRead(Selector.java:674) at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:576) at org.apache.kafka.common.network.Selector.poll(Selector.java:481) at org.apache.kafka.clients.NetworkClient.poll(NetworkClient.java:560) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.poll(ConsumerNetworkClient.java:265) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.pollNoWakeup(ConsumerNetworkClient.java:306) at org.apache.kafka.clients.consumer.internals.AbstractCoordinator.maybeLeaveGroup(AbstractCoordinator.java:1114) at org.apache.kafka.clients.consumer.internals.AbstractCoordinator.close(AbstractCoordinator.java:1074) at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.close(ConsumerCoordinator.java:980) at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2411) at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2378) at KafkaGeneratingUnitsRepository.getGeneratingUnits(KafkaGeneratingUnitsRepository.java:92) at actor.GeneratingUnitsConsumerActor.consumeGeneratingUnits(GeneratingUnitsConsumerActor.java:65) at actor.GeneratingUnitsConsumerActor$$Lambda$633/0x00000001005d2440.apply(Unknown Source) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:24) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:20) at scala.PartialFunction.applyOrElse(PartialFunction.scala:214) at scala.PartialFunction.applyOrElse$(PartialFunction.scala:213) at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:20) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:269) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:270) at akka.actor.Actor.aroundReceive(Actor.scala:537) at akka.actor.Actor.aroundReceive$(Actor.scala:535) at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:220) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580) at akka.actor.ActorCell.invoke(ActorCell.scala:548)

以及:

org.apache.kafka.common.KafkaException: Failed to close kafka consumer at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2430) at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2378) at repository.KafkaGeneratingUnitsRepository.getGeneratingUnits(KafkaGeneratingUnitsRepository.java:92) at actor.GeneratingUnitsConsumerActor.consumeGeneratingUnits(GeneratingUnitsConsumerActor.java:65) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:24) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:20) at scala.PartialFunction.applyOrElse(PartialFunction.scala:214) at scala.PartialFunction.applyOrElse$(PartialFunction.scala:213) at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:20) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:269) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:270) at akka.actor.Actor.aroundReceive(Actor.scala:537) at akka.actor.Actor.aroundReceive$(Actor.scala:535) at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:220) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580) at akka.actor.ActorCell.invoke(ActorCell.scala:548) at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270) at akka.dispatch.Mailbox.run(Mailbox.scala:231) at akka.dispatch.Mailbox.exec(Mailbox.scala:243) at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source) at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source) at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source) Caused by: java.lang.OutOfMemoryError: Java heap space at java.base/java.nio.HeapByteBuffer.(Unknown Source) at java.base/java.nio.ByteBuffer.allocate(Unknown Source) at org.apache.kafka.common.memory.MemoryPool$1.tryAllocate(MemoryPool.java:30) at org.apache.kafka.common.network.NetworkReceive.readFrom(NetworkReceive.java:113) at org.apache.kafka.common.network.KafkaChannel.receive(KafkaChannel.java:452) at org.apache.kafka.common.network.KafkaChannel.read(KafkaChannel.java:402) at org.apache.kafka.common.network.Selector.attemptRead(Selector.java:674) at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:576) at org.apache.kafka.common.network.Selector.poll(Selector.java:481) at org.apache.kafka.clients.NetworkClient.poll(NetworkClient.java:560) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.poll(ConsumerNetworkClient.java:265) at org.apache.kafka.clients.consumer.internals.ConsumerNetworkClient.pollNoWakeup(ConsumerNetworkClient.java:306) at org.apache.kafka.clients.consumer.internals.AbstractCoordinator.maybeLeaveGroup(AbstractCoordinator.java:1114) at org.apache.kafka.clients.consumer.internals.AbstractCoordinator.close(AbstractCoordinator.java:1074) at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.close(ConsumerCoordinator.java:980) at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2411) at org.apache.kafka.clients.consumer.KafkaConsumer.close(KafkaConsumer.java:2378) at repository.KafkaGeneratingUnitsRepository.getGeneratingUnits(KafkaGeneratingUnitsRepository.java:92) at actor.GeneratingUnitsConsumerActor.consumeGeneratingUnits(GeneratingUnitsConsumerActor.java:65) at actor.GeneratingUnitsConsumerActor$$Lambda$633/0x00000001005d2440.apply(Unknown Source) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:24) at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:20) at scala.PartialFunction.applyOrElse(PartialFunction.scala:214) at scala.PartialFunction.applyOrElse$(PartialFunction.scala:213) at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:20) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:269) at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:270) at akka.actor.Actor.aroundReceive(Actor.scala:537) at akka.actor.Actor.aroundReceive$(Actor.scala:535) at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:220) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:580) at akka.actor.ActorCell.invoke(ActorCell.scala:548)

查看Pod的内存使用情况(内存使用情况),看起来并不应该触发堆空间错误,请问该错误的可能原因是什么?


可能的原因分析

1. 瞬时内存尖峰未被监控捕获

监控面板展示的通常是时间段内的平均内存值,而OOM错误往往由短时间内的内存突增导致——比如Kafka消费者一次性拉取大量大消息,内存占用瞬间超过堆上限,这种尖峰可能在监控采样间隔中被平均掉,无法在面板上体现。

2. Kafka消费者配置不合理

从栈轨迹看,错误发生在Kafka消费者拉取消息的网络IO阶段(NetworkReceive.readFrom),可能的配置问题包括:

  • fetch.max.bytes/max.partition.fetch.bytes设置过大:消费者单次从Kafka拉取的消息总大小超过堆内存剩余空间;
  • max.poll.records值过高:一次性拉取过多消息到内存中处理,导致堆内存耗尽;
  • 内存池配置缺失:Kafka默认使用的内存池(MemoryPool$1)如果未合理配置,会频繁分配大ByteBuffer,加剧内存压力。

3. Akka Actor消息积压

Akka Actor的邮箱如果出现消息积压,会导致大量未处理的消息驻留在堆内存中。当Kafka消费者持续向Actor发送消息,而Actor处理速度跟不上时,邮箱中的消息会不断累积,最终引发堆内存不足。

4. 内存泄漏

  • 消费者资源未正确释放:栈轨迹显示关闭Kafka消费者时也触发了OOM,说明消费者实例可能未被及时回收,或存在重复创建消费者但未关闭的情况,导致内存中堆积大量消费者相关对象;
  • 业务对象内存泄漏:处理Kafka消息时生成的业务对象(如GeneratingUnits相关实例)未被正确回收,长期驻留在堆中(比如被静态集合引用、Actor状态未清理等)。

5. JVM堆内存与Pod资源不匹配

OpenShift中Pod的内存限制(resources.limits.memory)如果小于JVM堆内存(-Xmx)设置,会导致JVM尝试分配超过Pod可用的内存,触发OOM;反之,如果Pod内存限制远大于JVM堆内存,但JVM堆设置过小,也会在业务高峰时出现堆内存不足。需确认-Xmx、-Xms与Pod内存限制的配置比例是否合理(通常堆内存设置为Pod内存限制的50%-70%)。

6. 直接内存与堆内存的竞争

如果应用使用大量直接内存(Direct ByteBuffer),虽然直接内存不在堆中,但JVM总内存占用(堆+直接内存+元空间等)不能超过Pod的内存限制。当直接内存占用过高时,会挤压堆内存的可用空间,间接引发堆内存OOM。


内容的提问来源于stack exchange,提问作者hermanjakobsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:27:49