skip to content

What capacity does buffer() use by default, and what do the special Channel capacity constants mean in this context?

level: middleimportance: should knowfreq 40%

answer

  1. Default = Channel.BUFFERED = 64 (system-property overridable)
  2. RENDEZVOUS = 0, synchronous hand-off
  3. UNLIMITED = Int.MAX_VALUE, never blocks, OOM risk
  4. CONFLATED = keep latest, == buffer(1, DROP_OLDEST)
  5. CONFLATED + explicit overflow policy throws

basics

~20 s

By default buffer() holds about 64 items. You can also pass special values: 0 for no buffer (hand-off), a large unlimited buffer that never blocks, or a 1-slot conflated buffer that always keeps the newest value.

solid answer

~40 s

`buffer()`'s default capacity is `Channel.BUFFERED`, which resolves to **64** unless the `kotlinx.coroutines.channels.defaultBuffer` system property changes it. You can pass any non-negative `Int`, or one of the special constants: `Channel.RENDEZVOUS` (0) — a direct hand-off where the producer suspends until the collector takes each item (no real buffering); `Channel.UNLIMITED` (Int.MAX_VALUE) — an ever-growing buffer that never applies back-pressure (memory risk); `Channel.CONFLATED` — keeps only the latest value (equivalent to capacity 1 + DROP_OLDEST), and is mutually exclusive with an explicit non-SUSPEND policy. Capacity interacts with `onBufferOverflow`: a finite buffer plus SUSPEND yields classic bounded back-pressure; a finite buffer plus DROP_* trades data for a never-blocking producer. Picking the number is a latency/throughput/memory trade-off.

go deeper

for a junior

Knows buffer() has some default size and that you can pass a custom capacity.

for a middle

States the default is 64 and explains RENDEZVOUS / UNLIMITED / CONFLATED meanings.

for a senior

Knows the system-property override, the CONFLATED-plus-policy conflict, and the memory/latency trade-offs of sizing.

for a principal

Sets capacity policy across services, weighs OOM vs back-pressure risk, and accounts for operator fusion when reasoning about effective buffering.

## The default ```kotlin fun <T> Flow<T>.buffer( capacity: Int = Channel.BUFFERED, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND ): Flow<T> ``` The default `capacity` is `Channel.BUFFERED`, an alias that resolves to **64** at runtime (overridable via the JVM system property `kotlinx.coroutines.channels.defaultBuffer`). So a bare `buffer()` gives a 64-slot, back-pressured buffer. ## The special constants These come from the `Channel` companion and have specific integer encodings: - **`Channel.RENDEZVOUS` (0)** — no storage. Each `emit()` suspends until the collector is ready to receive that exact item; it is a synchronous hand-off. Producer and collector still run on separate coroutines (so they can interleave), but the producer can be at most one item ahead of nothing. - **`Channel.UNLIMITED` (`Int.MAX_VALUE`)** — effectively infinite. The producer **never** suspends and **never** drops, but the buffer can grow without bound, risking `OutOfMemoryError`. Overflow policy is irrelevant here. - **`Channel.CONFLATED`** — capacity that keeps only the **most recent** value; it behaves like `buffer(1, DROP_OLDEST)`. `conflate()` is the dedicated operator for this. - **`Channel.BUFFERED`** — the default, 64. ## Combining capacity with overflow ```kotlin flow.buffer() // 64 slots, SUSPEND flow.buffer(Channel.UNLIMITED) // never blocks, may OOM flow.buffer(0, BufferOverflow.SUSPEND) // rendezvous-like hand-off flow.buffer(16, BufferOverflow.DROP_OLDEST) // bounded, keep freshest 16 ``` Illegal/edge combos: passing `Channel.CONFLATED` together with an explicit non-default `onBufferOverflow` throws, because CONFLATED already implies an overflow behavior. Negative capacities other than the named constants throw `IllegalArgumentException`. ## Choosing a number - **Small capacity** → tighter coupling, lower memory, more back-pressure events. - **Large capacity** → more decoupling/throughput, higher memory and potential latency (stale items sitting in the queue). - **UNLIMITED** → only when you trust the producer to be bounded overall; otherwise it's a memory hazard. The operator also **fuses**: chained `buffer`/`flowOn` calls merge their channel rather than stacking multiple channels, and the largest requested capacity tends to win in the fused result.

  • What is the risk of Channel.UNLIMITED?
    The buffer grows without bound if the producer outpaces the collector, risking OutOfMemoryError; there is no back-pressure.
  • Can you override the default 64?
    Yes, globally via the kotlinx.coroutines.channels.defaultBuffer system property, or per-call by passing an explicit capacity.

saying these in an interview costs you the question

  • Saying the default capacity is 1 or unlimited
  • Believing RENDEZVOUS means a large buffer
  • Thinking UNLIMITED applies back-pressure
  • Combining CONFLATED with an explicit overflow policy without knowing it throws

context