Contact Us

When Kafka Runs Out of Memory Without Running Out of Memory 

September 3, 2026

Understanding one of Kafka’s least obvious failure modes 

If you’ve worked with Kafka long enough, you’ve probably seen a broker fail with an OutOfMemoryError. 

The natural assumption is that the broker has exhausted its available memory. The investigation usually starts with familiar questions: 

  • Is the JVM heap too small? 
  • Is there a memory leak? 
  • Has the container reached its memory limit? 
  • Is the node under memory pressure? 

Sometimes, however, every one of those questions leads to a dead end. 

This article explores one such scenario—a failure that initially looked like a classic memory issue, but ultimately had nothing to do with available RAM. Instead, the root cause was hidden in the interaction between Kafka’s storage engine and the operating system’s virtual memory management. 

Understanding why requires looking beyond the JVM. 

The Puzzle 

Imagine a Kafka broker that suddenly refuses to start. 

The exception is familiar: 

java.lang.OutOfMemoryError 

The response is equally familiar. 

Check the heap. 

Healthy. 

Check container memory. 

Healthy. 

Check node memory. 

Healthy. 

Increase JVM heap. 

No change. 

Increase Kubernetes memory limits. 

Still no change. 

At this point, the obvious explanations have been exhausted. Yet the broker still reports an OutOfMemoryError. 

So what resource has actually been exhausted? 

Looking Beyond the Heap 

One of Kafka’s greatest strengths is its storage architecture. 

Instead of constantly reading data through traditional I/O operations, Kafka relies heavily on memory-mapped files (mmap). This allows the operating system to efficiently cache and access log data while reducing unnecessary memory copies between user space and kernel space. 

Every Kafka log segment is backed by several files: 

000000000000.log 
000000000000.index 
000000000000.timeindex 

These files are memory mapped by the broker, allowing Kafka to access them efficiently while letting the operating system handle page caching. 

This design is one of the reasons Kafka achieves such high throughput. 

But it also introduces an important dependency that is often overlooked. 

Every memory-mapped file consumes a virtual memory mapping managed by the operating system. 

Unlike JVM heap, these mappings are not limited by Java’s heap size. 

They’re an operating system resource. 

When a Small Configuration Creates a Large Problem 

Now consider the following configuration: 

segment.ms = 10000 

At first glance, nothing appears unusual. 

Kafka simply rotates log segments every ten seconds. 

But let’s follow the consequences. 

A single partition now creates: 

  • 6 segments every minute 
  • 360 segments every hour 
  • 8,640 segments every day 

Every segment generates multiple files. 

Every file may be memory mapped. 

Multiply that across dozens or hundreds of partitions and a typical retention period, and what initially looked like a harmless configuration becomes millions of files that Kafka must manage. 

The JVM still has plenty of heap. 

The container still has available memory. 

The operating system, however, has a rapidly growing number of memory mappings to maintain. 

Eventually, that limit is reached. 

Why More Memory Doesn’t Help 

This is where the error message becomes misleading. 

The exception suggests a memory shortage. 

Naturally, engineers respond by increasing available memory. 

Unfortunately, memory was never the limiting factor. 

The limiting resource was the number of virtual memory mappings available to the Kafka process. 

Once that limit was reached, additional mmap() operations failed. 

From the JVM’s perspective, the failure surfaced as an OutOfMemoryError, even though physical memory remained available. 

The lesson is subtle but important: 

Not every OutOfMemoryError indicates that your application has exhausted RAM. 

Sometimes it indicates that the operating system can no longer provide another resource required to allocate memory. 

Understanding that distinction changes the entire direction of the investigation. 

Understanding the Chain of Events 

The interesting part wasn’t the configuration itself. 

It was understanding how a single Kafka setting propagated all the way down to the operating system. 

The chain looked like this: 

Aggressive segment rotation 
        ↓ 
More log segments 
        ↓ 
More .log and index files 
        ↓ 
More memory-mapped files 
        ↓ 
More virtual memory mappings 
        ↓ 
Operating system limit reached 
        ↓ 
mmap() fails 
        ↓ 
JVM reports OutOfMemoryError 
        ↓ 
Kafka broker cannot start 

Once the entire chain became clear, the solution became surprisingly straightforward. 

Correcting the segment rotation policy dramatically reduced the number of log segments. 

No JVM tuning was required. 

No larger Kubernetes nodes. 

No additional broker memory. 

Simply understanding which resource had actually been exhausted. 

Engineering Doesn’t Stop at the Fix 

Fixing the configuration resolved the immediate problem. 

But solving an incident once isn’t enough. 

The more important question became: 

How do we ensure we detect this long before a broker reaches that state again? 

Traditional Kafka dashboards typically focus on metrics such as: 

  • JVM memory 
  • CPU usage 
  • Consumer lag 
  • Disk utilization 
  • Network throughput 

Those metrics remained perfectly healthy throughout this scenario. 

The underlying resource was invisible. 

To close that observability gap, we introduced additional monitoring focused on the resources that actually mattered. 

Among the metrics we now track are: 

  • Log segment count per topic, allowing us to identify abnormal segment growth before it becomes operationally significant. 
  • Open file count per Kafka broker container, providing early visibility into resource consumption that isn’t reflected in traditional JVM metrics. 

These metrics complement—not replace—the standard Kafka dashboards. Together they provide a more complete picture of broker health and make this entire class of problems visible long before they affect availability. 

Looking Beyond Kafka 

Although this example involves Kafka, the lesson extends far beyond a single technology. 

Modern distributed systems depend on many operating system resources that rarely appear in standard dashboards. 

Heap memory is only one of them. 

Others include: 

  • File descriptors 
  • Virtual memory mappings 
  • Threads 
  • Ephemeral ports 
  • Inodes 
  • Network connection tracking 

These resources are usually invisible—until they’re exhausted. 

Building reliable platforms means understanding not only how applications behave, but also how they interact with the operating system underneath them. 

Sometimes the most valuable debugging tool isn’t adding more resources. 

It’s identifying which resource is actually running out. 

Final Thoughts 

The most interesting production problems are rarely solved by increasing limits or allocating larger machines. 

They’re solved by understanding how systems work beneath the abstraction layers we interact with every day. 

Kafka’s use of memory-mapped files is an elegant optimization that enables exceptional performance. But like every optimization, it introduces operational characteristics that engineers should understand when running Kafka in production. 

For us, this challenge reinforced a simple principle: 

The best long-term fixes don’t just solve today’s problem—they improve tomorrow’s observability. 

LATEST NEWS

View All

CONTACT US

    Name:*

    Email:*

    Company:*

    Subject:*

    Message:*

    Home | Articles | When Kafka Runs Out of Memory Without Running Out of Memory
    The website content is not intended for an audience under 18 years of age.
    Copyright © 2025 Delasport. All rights reserved.
    crossmenu