Hi, tl;dr: Flink batch streaming API job resident memory usage grows far beyond expectations, resulting in system OOM kill/JVM native memory allocation failure - would appreciate a look over our config/assumptions to potentially spot any obvious mistakes.
Longer form: My colleague and I are troubleshooting a large batch job for a long time, and still experience behaviour around flinks memory usage we cannot explain. My hope is that by explaining our configuration and observations, someone can spot a misconception. And in the ideal case I can then send a PR for the documentation to hopefully make that misconception less likely for other users. I'll start with an overview/"story-like" form, and then below that are some numbers/configs. This is a streaming job run in batch mode, processing terabytes of data sourcing and sinking to compressed files in S3. In between there are a few simple decoding and filter operations, then two processors with our main business logic and finally a few simple transformations and reduce steps. While reduce and sink writer tasks run, we encounter much more resident memory usage of the flink TM java process than expected from configuration, i.e. higher than the configured process memory. And that leads to failures, either the system OOM killer intervening or the JVM not being able to mmap. I know that the writers do use native memory, e.g. for avro deflate compression, which is a native method. Also the IO likely uses some native memory. We now configure 5g of task off-heap memory to compensate for any such native memory usage, but still encounter higher memory usage. Even 5g seems way too much for some compression buffers and IO, let alone more than that. So currently my main theory is that I misunderstand something about the memory related config. E.g. that task slots factor into used/allocated memory. We during the late stages of the job, i.e. during reduce and sink operations, we observe much higher memory usage than expected. The increase in memory usage isn't happening slowly, gradually over time, but quickly when those tasks start. This is an example of ps output for one TM: PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 14961 130 94.8 32213536 30957104 ? Sl Sep12 7453:03 /usr/lib/jvm/java-11-amazon-corretto.aarch64/bin/java -Xmx7435661840 -Xms7435661840 -XX:MaxDirectMemorySize=9663676416 -XX:MaxMetaspaceSize=268435456 -Dlog.file=/var/log/hadoop-yarn/containers/application_1724829444792_0007/container_1724829444792_0007_01_000330/taskmanager.log -Dlog4j.configuration=file:./log4j.properties -Dlog4j.configurationFile=file:./log4j.properties org.apache.flink.yarn.YarnTaskExecutorRunner -D taskmanager.memory.network.min=2147483648b -D taskmanager.cpu.cores=4.0 -D taskmanager.memory.task.off-heap.size=5368709120b -D taskmanager.memory.jvm-metaspace.size=268435456b -D external-resources=none -D taskmanager.memory.jvm-overhead.min=1073741824b -D taskmanager.memory.framework.off-heap.size=2147483648b -D taskmanager.memory.network.max=2147483648b -D taskmanager.memory.framework.heap.size=134217728b -D taskmanager.memory.managed.size=7328288240b -D taskmanager.memory.task.heap.size=7301444112b -D taskmanager.numberOfTaskSlots=4 -D taskmanager.memory.jvm-overhead.max=1073741824b --configDir . -Djobmanager.rpc.address=ip-172-30-119-251.us-west-2.compute.internal -Dweb.port=0 -Djobmanager.memory.off-heap.size=134217728b -Dweb.tmpdir=/tmp/flink-web-81fb345d-de64-4002-bd78-4454ca901566 -Djobmanager.rpc.port=42195 -Drest.address=ip-172-30-119-251.us-west-2.compute.internal -Djobmanager.memory.jvm-overhead.max=322122552b -Djobmanager.memory.jvm-overhead.min=322122552b -Dtaskmanager.resource-id=container_1724829444792_0007_01_000330 -Dinternal.taskmanager.resource-id.metadata=ip-172-30-116-113.us-west-2.compute.internal:8041 -Djobmanager.memory.jvm-metaspace.size=268435456b -Djobmanager.memory.heap.size=2496449736b For easier readability: RSS = 30957104kB = 29.5GB Then this is the relevant bit of our config file. It includes explanations of how we came up with those numbers, so here's where I hope someone can quickly tell me where I am wrong :) # We configure yarn to provide 24g to containers, rest for system. It turns out # yarn doesn't enforce that memory limit though (we exceeded it at times). taskmanager.memory.process.size: 24g # We have 4 cores on the machine, so the flink default of task slots == vcpu is # fine (as in we make use of all cores). taskmanager.numberOfTaskSlots: 4 # Flink memory calculation/provisioning isn't entirely straight-forward, thus # explaining steps here (best resource: # https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/deployment/memory/mem_setup_tm/#detailed-memory-model). # # Settings from 2022 (partly based on flink documentation recommendations): # Enable sort-based blocking shuffle and associated config recommendations. # This will become the default in 1.15. # https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/ops/batch/blocking_shuffle/ taskmanager.network.blocking-shuffle.compression.enabled: true taskmanager.network.sort-shuffle.min-parallelism: 1 # batch-shuffle is taken from overall framework.off-heap, i.e. the latter needs # to be at least as big as the former. taskmanager.memory.framework.off-heap.batch-shuffle.size: 1024m taskmanager.memory.framework.off-heap.size: 2g # Number of 32K buffers (taskmanager.memory.segment-size), taken from network memory. taskmanager.network.sort-shuffle.min-buffers: 2048 # Not sure why/if we need this, e.g. if it's related to the above. Failures # happened when removing it :) taskmanager.memory.network.min: 2g taskmanager.memory.network.max: 2g # We leave jvm settings at defaults: # flink = process - jvm-overhead - jvm-metaspace = 24g - 1g - 0.25g = 22.75g # # OOMs happened due to memory usage not part of flink (managed, network) or JVM # heap - some native allocations. To avoid that we reserve ample non-heap task # memory (memory flink will leave free). This might be too much, we didn't # optimize once it ran fine: taskmanager.memory.task.off-heap.size: 5g # Managed memory (useful for efficient flink data operations, e.g. sorting, # caching) and heap remain to be set: # flink = heap + managed + direct = heap + managed + network + framework + task-off-heap # = heap + managed + 2g + 2g + 5g # -> heap + managed = 22.75g - 2g - 2g - 5g = 13.75g # Metrics showed heap usage resp. garbage collection activity is very low at # ~10g, so it's fine to go even a bit lower in favor of more managed memory # (used by flink for various operations directly on raw data e.g. sorting, # caching, and documented to be needed/useful in batch mode): taskmanager.memory.managed.fraction: 0.2f # heap = 13.75g - flink * managed-fraction = 13.75g - 22.75g * 0.2 = 9.2g And just in case, listing a few things we do not see/have problems with: Heap memory usage/GC activity is low. No errors about missing network buffers or too little managed memory. All metrics we get from flink/JVM look perfectly fine, it's just the java process resident memory usage that is growing beyond what we expect and the following system level OOM. I'd be immensely grateful for any pointers/feedback on the above. And I hope I can repay the kindness by improving the docs (memory or config), if any learnings apply to them. Best, Simon