Home › ML / AI › TensorFlow OOM — Fix ResourceExhaustedError
Intermediate 6 min · September 23, 2026

TensorFlow OOM — Fix ResourceExhaustedError

Fix TensorFlow OOM: read the failed tensor shape, cut batch size, enable memory growth, use mixed precision, and stream inputs..

N
Naren Founder & Principal Engineer

20+ years shipping production ML systems and the infrastructure behind them. Drawn from code that ran under real load.

Follow
✓ Production
production tested
September 24, 2026
last updated
1,942
articles · all by Naren
Before you start⏱ 10 min
  • ✓Python 3 with TensorFlow 2.x and a GPU (or Colab) to follow along
  • ✓CNN or transformer training basics: batches, epochs, optimizers
  • ✓nvidia-smi access to read GPU memory on your machine
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • ResourceExhaustedError names the exact failed tensor in shape[...] — multiply its elements by dtype bytes to size the problem
  • Batch size is the fastest fix: halve it, then use gradient accumulation to keep the effective batch and learning rate valid
  • TensorFlow pre-allocates nearly all GPU memory by default — enable memory growth so jobs can share a card
  • Mixed precision (float16 compute, float32 master weights) roughly halves memory; switch it on before shrinking the model
  • Check host RAM too: an input pipeline with .cache() can OOM the container while the GPU sits half empty
✦ Definition~90s read
What is TensorFlow OOM Fix?

ResourceExhaustedError with an OOM note is TensorFlow's error for a failed allocation on GPU (occasionally CPU or host RAM): one tensor didn't fit the remaining memory. The message names the failed tensor's shape and dtype plus the device, and the surrounding log dumps the BFC allocator's state — reserved bytes, in-use bytes, free chunks.

★
Think of GPU memory as a small, fast workbench and your training as a cooking project.

It kills the op (usually the step, often the process) the moment the budget breaks; there is no partial progress or graceful fallback. Variants include CUDA_ERROR_OUT_OF_MEMORY from the driver layer and container-level OOM-kills when host RAM, not VRAM, overflows.

Six causes cover nearly every case. Oversized batches inflate per-step activations linearly. Default pre-allocation reserves the whole card and starves neighbors. Oversized models (weights plus gradients plus optimizer moments) exceed VRAM at any batch.

Full-precision training spends twice the bytes mixed precision needs. Bloated input pipelines park the dataset in host RAM. And variable shapes fragment the allocator so free memory isn't contiguous. The failed tensor's identity — activation versus weight versus optimizer state — points at the right cause before any code changes.

Fixes rank by leverage: read the shape, cut batch (with accumulation), enable growth or caps, switch on mixed precision, right-size model or optimizer, stream the pipeline. Each is a budgeted engineering choice, not a sacrifice — and together they fit most workloads onto cards teams assumed were too small.

Plain-English First

Think of GPU memory as a small, fast workbench and your training as a cooking project. Error OOM means the dish you tried to set down is bigger than the bench — TensorFlow tells you its exact measurements in the error. Fixes map to kitchen sense: cook in smaller batches, share the bench instead of hogging it (memory growth), use lighter pans (mixed precision), pick recipes that fit the kitchen (smaller model), and stop piling all groceries on the counter at once (stream the input pipeline).

Your training run dies 40 minutes in with a wall of text ending in ResourceExhaustedError: OOM when allocating tensor with shape[32,512,128,128] and type float on /job:localhost/replica:0/task:0/device:GPU:0. No stack trace points at your code. The shapes look innocent. Restarting just fails again at the same step.

This error means one GPU allocation didn't fit — that's all. But which allocation, and why now, hides in the details: the bracketed shape names the exact tensor that broke the budget, and the allocator log above it shows everything already resident. Reading those two pieces turns a panic into arithmetic: shape times dtype bytes versus free VRAM.

The usual suspects form a short lineup: a batch too big for the card, TensorFlow's default grab of nearly all GPU memory starving everything else, an input pipeline buffering gigabytes in host RAM, float32 where mixed precision would halve the cost, or simply a model bigger than the hardware. Each has a different fix, and the wrong one — shrinking a model when the batch was the problem — wastes days.

This guide teaches the reading order: decode the shape in the message, cut batch size intelligently, tame pre-allocation with growth and caps, switch on mixed precision safely, right-size the model, and stream the input pipeline. You'll turn OOM from a feared crash into a budgeted resource.

Read the Shape in the Message

The error message is a measurement, not a complaint: ResourceExhaustedError: OOM when allocating tensor with shape[32,512,128,128] and type float on device GPU:0. The shape is the failed allocation's dimensions in elements — here 32 times 512 times 128 times 128, about 268 million numbers. The type sets bytes each: 4 for float32, 2 for float16, 1 for int8. Multiply: this tensor alone wanted roughly 1GB, and the allocator couldn't find it contiguous.

Above the error sits the allocator log — lines showing total reserved bytes, bytes in use, and the largest free chunk. Reserved near 100% with modest in-use bytes means fragmentation or pre-allocation overhead, not a truly full card. A largest-free-chunk smaller than your tensor means the memory exists but not contiguously, which points at variable shapes rather than sheer size.

The device tag matters too. GPU:0 tags blame device memory; CPU tags blame host RAM and exonerate the card entirely. Teams that skip this line tune batch sizes for a host-RAM problem and wonder why nothing changes. Read device first, shape second, allocator third — that order never wastes time.

Do the arithmetic on paper before changing code. Elements times bytes gives the failed tensor; parameter counts times optimizer multipliers give the resident floor. Most OOMs resolve the moment someone multiplies — the fix becomes obvious when the numbers are visible.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tensorflow as tf

# Must run before ANY tensor op: report growth + log the real budget.
gpus = tf.config.list_physical_devices("GPU")
if gpus:
    tf.config.experimental.set_memory_growth(gpus[0], True)

print("logical GPUs:", tf.config.list_logical_devices("GPU"))
# After a failure, read the peak to size the next attempt:
# info = tf.config.experimental.get_memory_info("GPU:0")
# print("peak bytes:", info["peak"], "current:", info["current"])

# Size a suspect tensor on paper, in code:
import math
shape = (32, 512, 128, 128)
print("float32 GB:", math.prod(shape) * 4 / 1e9)  # ~1.07
📊 Production Insight
Screenshot the full error including the allocator dump into the incident ticket. Teams that paste only the last line re-derive the numbers every shift; the dump is the whole diagnosis.
🎯 Key Takeaway
Elements times dtype bytes is the failed allocation's size — read device, shape, and allocator log before touching any code.

Batch Size: The Fastest Lever

Batch size scales activation memory almost linearly, which makes it the highest-leverage knob: halving the batch roughly halves the activation footprint of every layer at once. When the OOM names an activation tensor — shapes with the batch dimension first, like [32,512,128,128] — cut the batch first and ask questions later. Drop from 64 to 32, rerun, and watch the allocator peak fall.

But batch size also steers optimization: smaller batches mean noisier gradients and more steps per epoch, which shifts the learning-rate sweet spot. Naive halving can clear the OOM while silently degrading the model. The professional fix pairs the cut with gradient accumulation — run N micro-batches, sum their gradients, apply one update — restoring the original effective batch exactly while paying only the micro-batch's memory.

Sequence length and image resolution deserve the same scrutiny because they multiply the same activations. Doubling side length quadruples every spatial activation; doubling context length quadruples attention matrices. A resolution bump from 224 to 384 nearly triples activation memory with zero new parameters — the classic invisible OOM behind preprocessing pull requests.

Settle batch size from measured headroom, not habit. Read the allocator peak at a candidate batch, keep 10-20% reserve for allocator overhead and eval passes, and record the chosen size beside the card model in the run config.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import tensorflow as tf

# Halve the per-step batch, accumulate 4 micro-steps -> same effective batch.
base_batch, accum_steps = 64, 4
micro_batch = base_batch // accum_steps  # 16 fits the card

dataset = dataset.batch(micro_batch).prefetch(tf.data.AUTOTUNE)
optimizer = tf.keras.optimizers.Adam(1e-3)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

@tf.function
def micro_grads(xb, yb):
    with tf.GradientTape() as tape:
        loss = loss_fn(yb, model(xb, training=True))
    return loss, tape.gradient(loss, model.trainable_variables)

accum = [tf.zeros_like(v) for v in model.trainable_variables]
for step, (xb, yb) in enumerate(dataset):
    loss, g = micro_grads(xb, yb)
    accum = [a + x / accum_steps for a, x in zip(accum, g)]
    if (step + 1) % accum_steps == 0:
        optimizer.apply_gradients(zip(accum, model.trainable_variables))
        accum = [tf.zeros_like(v) for v in model.trainable_variables]
📊 Production Insight
Eval passes OOM where training fits: validation often runs larger batches without gradients but with full activations. Size the batch for eval's peak, or cap eval batch separately.
🎯 Key Takeaway
Halve the batch to fit, accumulate gradients to preserve dynamics — and audit resolution and sequence length as batch multipliers.

Growth vs Pre-allocation: Share the Card

TensorFlow's default GPU behavior shocks newcomers: the first process reserves nearly all device memory at startup, whether it needs it or not. This upfront grab reduces fragmentation and speeds allocation, but on shared cards it means job one starves job two — a debugger, a second experiment, or an inference sidecar — which then OOMs on an apparently idle GPU. nvidia-smi shows the truth: one process holding gigabytes it never touches.

Memory growth fixes the sharing: tf.config.experimental.set_memory_growth(gpu, True) makes TensorFlow allocate as needed instead of upfront. Every process takes what it uses, and cards become shareable. The call must precede any tensor creation — put it at the very top of the entrypoint, before model building and dataset construction, or it raises and does nothing.

For hard partitioning, virtual device caps slice one card into bounded lanes: set_virtual_device_configuration with memory_limit in MB gives each process a ceiling it cannot cross. This is the right tool for CI runners and shared dev boxes where noisy neighbors must be contained, not just politely sharing. Exceeding your lane OOMs cleanly instead of starving others silently.

Note the trade-off honestly: growth can increase fragmentation under rapidly varying shapes, slightly raising peak versus upfront reservation. For single-job dedicated cards, the default grab is fine and sometimes leaner. Choose per machine: dedicated trainers keep defaults, shared boxes get growth or caps — and record the choice in the run config.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
import tensorflow as tf

# Share a card politely: growth MUST come before any tensor op.
gpus = tf.config.list_physical_devices("GPU")
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)

# Or partition strictly (e.g. 8GB lane on a shared CI card):
# tf.config.set_virtual_device_configuration(
#     gpus[0],
#     [tf.config.LogicalDeviceConfiguration(memory_limit=8192)])

print("growth on:", [tf.config.experimental.get_memory_growth(g) for g in gpus])
📊 Production Insight
Sidecar OOMs are the tell: when inference dies every time training starts on a shared box, training's upfront grab is the killer — growth on the trainer fixes someone else's crash.
🎯 Key Takeaway
Defaults grab the whole card; growth shares it politely and caps partition it strictly — pick per machine and configure first.

Mixed Precision: Halve Memory in Two Lines

Mixed precision is the closest thing to free memory in deep learning: compute in float16, keep a float32 master copy of weights, and watch most tensors halve. Activations, gradients, and optimizer moments shrink dramatically while final accuracy typically matches float32 within noise — modern accelerators even run fp16 math faster, so the memory fix arrives with a speed bonus.

Enable it in two lines with a policy plus loss scaling: tf.keras.mixed_precision.set_global_policy('mixed_float16') routes compute to fp16, and LossScaleOptimizer guards the backward pass against fp16 underflow by scaling the loss up before backprop and down afterward. Skipping loss scaling is the classic footgun — tiny gradients flush to zero in fp16, training silently stalls, and the team blames the memory fix for a numerics bug.

Verify the switch worked: model summaries show fp16 compute dtypes beside fp32 variables, allocator peaks drop roughly 30-50%, and loss curves track the fp32 baseline run. If NaNs appear, they're a scaling or epsilon issue (raise the loss scale, check batch-norm placement), not a reason to abandon precision — fp32 master weights already protect the update step.

Treat mixed precision as the default for new runs on capable hardware, not an emergency lever. Enabling it before the first OOM means the budget starts halved; enabling it after means re-validating a training recipe mid-crisis. The two-line change belongs in the template, not the incident thread.

PYTHON
1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf
from tensorflow.keras import mixed_precision

# Two-line switch: fp16 compute, fp32 master weights, scaled loss.
mixed_precision.set_global_policy("mixed_float16")
optimizer = mixed_precision.LossScaleOptimizer(tf.keras.optimizers.Adam(1e-3))
model.compile(optimizer=optimizer, loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

print("compute dtype:", model.layers[0].compute_dtype)  # float16
print("variable dtype:", model.layers[0].variable_dtype)  # float32
📊 Production Insight
Batch-norm and softmax keep fp32 even under the fp16 policy — that selectivity is why accuracy survives. Teams that force every op to fp16 manually lose the exact protection the policy provides.
🎯 Key Takeaway
Two lines — fp16 policy plus loss scaling — halve memory and often speed training; make it the template default.

Right-Size the Model and Optimizer

Sometimes the model genuinely exceeds the card, and honesty beats hope. Budget it: parameters times bytes per element for weights, the same again for gradients, and twice more for Adam's two moment estimates — roughly 4x parameters in fp32 (2x for SGD, less with Adafactor or 8-bit optimizers). Add per-step activations from the allocator log. If that total exceeds VRAM at batch size 1 with fp16 on, no pipeline trick fits it — the architecture must change.

Shrink in order of accuracy cost. First, swap the optimizer: Adam to SGD or Adafactor sheds moment memory with modest tuning cost. Second, trim width before depth — narrower layers cut parameters quadratically with small accuracy impact. Third, freeze and share: frozen backbones need no gradients or moments, and gradient checkpointing recomputes activations instead of storing them (slower steps, far less memory). Depth cuts and tiny embeddings come last, since they change model character most.

Alternatively, shard instead of shrink: model-parallel splits, pipeline stages, or fully-sharded data parallel spread one model across cards. These are engineering projects, not config tweaks — adopt them when the model must stay big, not to avoid a batch-size conversation.

Record the budget beside the run: params, dtype, optimizer multiplier, batch, peak bytes, card model. Next quarter's capacity planning starts from arithmetic, not folklore.

⚠ Don't Resize Until the Math Fails
Shrinking a model that fits wastes weeks of capacity work; starving a model that doesn't wastes the run. Do the parameter arithmetic first — count_params times dtype bytes times optimizer multiplier — and only resize when the math fails.
📊 Production Insight
Frozen backbones are the unsung memory win in transfer learning: no gradients, no moments, no activation storage for frozen layers — fine-tuning head-only fits cards the full model never could.
🎯 Key Takeaway
4x params for Adam in fp32 is the floor — shed optimizer moments first, width second, and shard only when bigness is mandatory.

Stream the Input Pipeline

A training job uses two memories, and OOM can strike either. Host RAM feeds the input pipeline: decoded images, tokenized batches, shuffled buffers, and .cache() contents all live there. When the container dies while nvidia-smi shows a half-empty GPU, the input pipeline — not the model — is the killer, and every GPU-side tweak misses.

The usual culprit is .cache() without a filename, which materializes the entire preprocessed dataset in host RAM after the first epoch. On large datasets that means tens of gigabytes appearing exactly when training looks healthiest — epoch two. Fix it by caching to sharded files, caching a smaller pre-batched stage, or dropping the cache for streaming with prefetch. Order matters too: .batch() before .map() materializes giant raw batches; .map() then .batch() keeps elements small until the end.

Build the pipeline to stream: interleave file reads across shards, prefetch with tf.data.AUTOTUNE to overlap host preprocessing with device compute, and vectorize maps so per-element Python overhead doesn't inflate buffers. Monitor host RAM (docker stats, free -m) on the same dashboard as GPU bytes — two lines that together pinpoint the pool in seconds.

Size shuffle buffers deliberately: a 10k shuffle buffer of 4K images holds gigabytes. Match the buffer to the mixing your training actually needs — full-dataset shuffling is rarely worth its RAM, and file-level plus modest buffer shuffling converges the same.

📊 Production Insight
Epoch-two deaths are the signature: epoch one fills the in-memory cache while fitting fine, then epoch two's first batch plus the full cache exceeds the cgroup — always suspect .cache() when the crash waits one epoch.
🎯 Key Takeaway
Stream, don't warehouse: prefetch to overlap, shard or drop .cache(), and watch host RAM on the same dashboard as GPU bytes.
● Production incidentPOST-MORTEMseverity: high

A Resolution Bump Tripled Activations and OOMed Nightly Retrains

Symptom
A nightly retraining job that had run green for months began dying with ResourceExhaustedError 40 minutes in, every night for a week. The error named an activation tensor with a spatial shape nobody recognized, and restarts failed at the same step. GPU dashboards showed reserved memory pinned near 100% while the team argued about model size.
Assumption
The team blamed the model. The new release had added two transformer layers, so capacity planning took the heat and a week went into distilling a smaller variant. Nobody measured first — the parameter math would have shown the two layers cost under 400MB on a 40GB card. The real change (resolution bump in preprocessing) sat in a different pull request, reviewed by a different person, and never entered the discussion.
Root cause
A preprocessing pull request raised input resolution from 224 to 384 pixels to chase accuracy, nearly tripling activation memory per sample (quadratic in side length). The batch size tuned for 224px no longer fit, and the failing tensor in the message — shape[64,256,96,96] float32 — was an early convolution activation, not a weight. The model weights hadn't grown at all; the per-sample activations had, and 40 minutes of warmup hid the crash until the first full-size epoch hit.
Fix
The immediate fix was reverting the loader to 224px plus prefetch tuning, restoring the step time and clearing the OOM in one deploy. The durable fixes: input resolution joined the model config as a first-class hyperparameter, CI asserts estimated activation memory against the target card before launching runs, and training dashboards plot allocator peak bytes per epoch so memory creep pages before it kills.
Key lesson
  • Measure the failed tensor before blaming the model — activation math beats architecture suspicion every time.
  • Input resolution is a memory hyperparameter — review preprocessing changes with the same rigor as model changes.
  • Track allocator peaks as a metric, not a log line — creeping bytes are a paging alert, not trivia.
Production debug guideMatch the symptom to the memory pool that's actually exhausted — GPU, host, or allocator overhead.5 entries
Symptom · 01
OOM names a batch-scaled activation tensor mid-training
→
Fix
Read the shape[...] and type in the error, multiply elements by dtype bytes (4 for float, 2 for half), and compare against free VRAM from nvidia-smi --query-gpu=memory.free --format=csv. Then halve the batch size and rerun: if the error clears, batch was the cause. Restore large-step behavior with gradient accumulation (e.g. 4 micro-steps) so learning-rate schedules stay valid.
Symptom · 02
First job fine, every concurrent job OOMs on the same card
→
Fix
Run nvidia-smi and check whether one python process holds nearly all VRAM while using little of it (reserved far above used). Add tf.config.experimental.set_memory_growth(gpus[0], True) before any tensor is created — it must run first thing — or cap sharing with set_virtual_device_configuration and a memory_limit in MB. Rerun nvidia-smi to confirm two processes now coexist.
Symptom · 03
OOM on step one naming a giant weight or optimizer tensor
→
Fix
Count parameters with model.count_params(), multiply by 4 bytes and by 4x for Adam (weights, grads, two moments) to get the floor, then add per-step activations from the allocator log. If the floor alone exceeds VRAM, no batch size saves you: switch on mixed precision (halves most terms), freeze or shrink layers, or move from Adam to SGD/Adafactor for fewer moments.
Symptom · 04
Container OOM-killed while GPU memory looks healthy
→
Fix
Watch host RAM with free -m or docker stats during the first epochs while nvidia-smi stays flat. If host RAM climbs toward the cgroup limit, grep the pipeline for .cache() without a filename (in-memory cache of the full dataset) and for .batch() before .map() (materializing giant batches). Restream: .cache('/tmp/cache') sharded or removed, interleave file reads, and add .prefetch(tf.data.AUTOTUNE).
Symptom · 05
OOM appears after hours of healthy training
→
Fix
Log tf.config.experimental.get_memory_info('GPU:0')['peak'] per epoch alongside batch size, sequence length, and input resolution. When peaks creep up across epochs, suspect variable-length inputs fragmenting the BFC allocator or a growing cache. Stabilize shapes by bucketing sequence lengths and padding per bucket, then re-baseline the peak before changing anything else.
TensorFlow OOM Causes — Confirm and Fix Each
Root CauseHow to ConfirmFixPrevention
Batch size exceeds free GPU memoryHalving batch size clears the error; allocator log shows one giant tensorReduce batch size and accumulate gradients to keep throughputSize batches from nvidia-smi headroom, not from a tutorial default
TF pre-allocates nearly all GPU memoryA second process OOMs although the first uses little; growth flag unsetEnable memory growth or a per-process memory capSet growth or caps in every training entrypoint by default
Oversized model or optimizer state for the cardParameter count times bytes times 4 (weights, grads, Adam states) exceeds VRAMShrink width/depth, freeze layers, or switch Adam to SGD/AdafactorBudget memory on paper before launching week-long runs
Input pipeline buffers the dataset in RAMHost RAM climbs while GPU idles; .cache() without sharding suspectedStream with prefetch, drop in-memory .cache(), shard cached filesProfile host RAM alongside GPU RAM in every training dashboard
⚙ Quick Reference
3 commands from this guide
FileCommand / CodePurpose
gpus = tf.config.list_physical_devices("GPU")Read the Shape in the Message
base_batch, accum_steps = 64, 4Batch Size
from tensorflow.keras import mixed_precisionMixed Precision

Key takeaways

1
Read the shape in the message
it names the exact tensor that broke the budget, so shrink that first.
2
Batch size is the fastest lever
halve it, then restore step semantics with gradient accumulation.
3
Enable memory growth or per-process caps everywhere
default pre-allocation starves shared cards.
4
Mixed precision halves memory for free-ish
flip it on with loss scaling before shrinking models.
5
Budget weights, grads, optimizer state, and activations on paper
never launch a model that fails arithmetic.
6
Stream the input pipeline
prefetch and shard caches so host RAM doesn't kill what the GPU could run.

Common mistakes to avoid

5 patterns
×

Ignoring the tensor shape printed in the error

Symptom
Random cuts to batch size, model width, and workers that never quite fix the OOM, because the actual giant — a 4K image batch or a 8K sequence — was never identified.
Fix
Read the bracketed shape, multiply out its bytes, and shrink that tensor first — usually batch size, sequence length, or image resolution. The message already did the hardest math for you.
×

Cutting batch size without compensating training dynamics

Symptom
OOM clears but validation accuracy drops or training slows 3x — the effective batch changed the optimization trajectory and nobody adjusted learning rate or steps.
Fix
Halve the batch size until it fits, then restore step semantics with gradient accumulation. Log tokens or samples per second to confirm throughput survived the change.
×

Letting TensorFlow grab all GPU memory by default

Symptom
The first job runs fine while every concurrent job — debugger, second experiment, inference sidecar — dies with OOM on an apparently empty card.
Fix
Call set_memory_growth on every GPU at startup (or set a hard per-process cap for shared cards). Re-run nvidia-smi to confirm two processes now coexist instead of one starving the other.
×

Blaming the GPU for a host-RAM input pipeline blowup

Symptom
GPU memory looks fine in nvidia-smi while the container gets OOM-killed by the host — the dataset cache, not the model, ate everything.
Fix
Inspect the message for CPU (not GPU) allocator tags and host-RAM growth, then stream the pipeline: prefetch, interleave reads, and shard or drop .cache(). Keep the GPU fed without warehousing the dataset in RAM.
×

Launching a model that never fit the card

Symptom
OOM on step one with a huge weight tensor in the message — days of queue time and setup burned on a run whose parameter math failed on paper.
Fix
Estimate weights plus gradients plus optimizer state (4x params for Adam in fp32) before launching, then enable mixed precision to halve most of it. If the math doesn't fit the card, shrink the model before the first epoch.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Your run dies with ResourceExhaustedError. What's your first move?
Q02JUNIOR
Why does TensorFlow grab all GPU memory, and how do you share a card?
Q03SENIOR
How do you budget GPU memory for a model plus Adam before launching?
Q04SENIOR
How does gradient accumulation preserve large-batch training on a small ...
Q05SENIOR
GPU looks fine but the container still gets OOM-killed. How do you diagn...
Q01 of 05JUNIOR

Your run dies with ResourceExhaustedError. What's your first move?

ANSWER
I'd multiply out the named shape by its dtype bytes to see the failed allocation's size, then check the allocator log for what's already resident. Usually the fix is halving the batch size, enabling memory growth, or switching on mixed precision — in that order, since batch and growth are one-line changes.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
How do I tell OOM apart from mixed-precision NaNs?
02
Can XLA compilation itself cause OOM?
03
When should I use gradient checkpointing?
04
Will adding more GPUs fix my OOM?
05
What does allocator fragmentation look like?
06
Should I budget against total VRAM or free VRAM?
N
Naren Founder & Principal Engineer

20+ years shipping production ML systems and the infrastructure behind them. Drawn from code that ran under real load.

Follow
✓ Verified
production tested
September 24, 2026
last updated
1,942
articles · all by Naren
🔥

That's TensorFlow & Keras. Mark it forged?

6 min read · try the examples if you haven't

←
Previous
Sklearn NotFittedError Fix
11 / 11 · TensorFlow & Keras