【问题标题】:Akka Stream application using more memory than the jvm's heapAkka Stream 应用程序使用比 jvm 堆更多的内存
【发布时间】:2022-12-09 00:27:49
【问题描述】:
概括:
我有一个使用 akka 流的 Java 应用程序,它使用的内存比我指定要使用的 jvm 多。以下值是我通过 JAVA_OPTS 设置的。
- 最大堆大小 (-Xmx) = 700MB
- 元空间 (-XX) = 250MB
- 堆栈大小 (-Xss) = 1025kb
使用这些值并将它们代入下面的公式,可以假设应用程序将使用大约 950MB。然而事实并非如此,它使用了超过 1.5GB 的空间。
最大内存 = [-Xmx] + [-XX:MetaspaceSize] + number_of_threads * [-Xss]
问题:关于这怎么可能的想法?
应用概述:
此 java 应用程序使用 alpakka 连接到 pubsub 并使用消息。它利用 akka 流的并行性,在其中对消费的消息执行逻辑,然后将这些消息生成到 kafka 实例。请参阅下面的堆转储。请注意,堆只有 912.9MB,所以有些东西占用了 587.1MB 并使内存使用量超过 1.5GB
为什么这是个问题?
此应用程序部署在 kubernetes 集群上,POD 的内存限制指定为 1.5GB。因此,当运行 java 应用程序的容器消耗超过 1.5GB 时,容器将被终止并重新启动。
【问题讨论】:
标签:
jvm
akka
akka-stream
akka-http
alpakka
【解决方案1】:
简短的回答是,这些并不能说明 JVM 消耗的所有内存。
例如,在堆之外,内存分配用于:
- 压缩类空间(由
MaxMetaspaceSize管理)
- 直接字节缓冲区(特别是如果您的应用程序执行网络 I/O 并关心性能,几乎可以肯定会大量使用它们)
- 线程(每个thread 有一个由
-Xss管理的堆栈...请注意,如果混合不同的并发模型,每个模型将倾向于分配自己的线程,而不一定提供共享线程的方法)
- 如果涉及本机代码(例如,可能在 Alpakka 用于与 pubsub 交互的库中?),可以在堆外分配任意数量的内存)
- 代码缓存(通常为 48MB)
- 垃圾收集器的状态(将根据使用中的 GC 的不同而有所不同,包括是否存在任何可调选项)
- 其他各种一般不会那么大的东西
根据我的经验,您通常使用最多的堆(pod 内存限制负 1 GB)是相当安全的,但是如果您正在执行非常大的 I/O 等,即使那样您也很容易获得 OOM。
您的 JVM 可能附带对 native memory tracking 的支持,这至少可以揭示一些非堆消耗:这些分配中的大部分往往在应用程序完全加载后不久发生,因此以更高的资源限制运行,然后停止(例如通过 SIGTERM 有足够的时间让它保存结果)应该让你知道你正在处理什么。