You’ll never need memory dumps. Until you do. And then it’s the only tool that saves you.

In our microservice architecture, we have gRPC services talking to each other via gRPC streams. During acceptance testing, QA reported that certain features would hang indefinitely. No error messages. No exceptions. No stack traces in the logs. The system just… stopped responding.

The backend team started doing what everyone does: trial and error. Disable feature A, test again. Disable feature B. Toggle this setting. Eventually they’d stumble on the answer through elimination.

But we didn’t have time for that. So I captured a memory dump.

I opened it, looked at the C# task stack traces, and saw immediately: one feature was opening gRPC channels and never closing them. HTTP/2 has a hard limit on simultaneous open stream channels per connection. The code hit that limit, tried to allocate another channel, and hung waiting for one to become available. Meanwhile, other functionality was still running, so there were no obvious error signals—just a quiet deadlock that only the task stacks would reveal.

One dump file. Thirty seconds of analysis. Problem solved.

That’s why memory dumps matter.

What Are Memory Dumps?

A memory dump is a snapshot of your application’s memory at a specific moment in time. Everything: all the objects on the heap, all the variables, all the threads and their stacks, the state of the garbage collector, locks, everything.

In that gRPC case, the dump told me exactly which tasks were running and where they were stuck. No guessing. No trial and error. Just the facts.

The technical term is post-mortem debugging—you’re analyzing your application after something went wrong, without being able to attach a debugger and step through code in real time.

When You Need a Memory Dump

Memory dumps are for the problems that don’t show up in logs or metrics. They’re for:

  • Hangs with no error — like the gRPC channel limit issue. The code isn’t crashing or throwing exceptions. It’s just stuck. The task stacks tell you exactly where.
  • Memory leaks — you can see what objects are still alive, what’s referencing them, how much memory they’re using
  • Crashes in production — when you can’t attach a debugger, a dump gives you the entire state of the process at the moment it died
  • Mysterious performance cliffs — GC pauses, lock contention, thread starvation—all visible in a dump
  • Deadlocks — which threads are waiting on what, which locks are held by whom

Without a dump in that gRPC case, we would have spent days guessing. With one, the answer was obvious.

How to Read a Memory Dump

.NET Dumps: Windows

The easiest approach for .NET dumps on Windows is Visual Studio. Open the dump file directly in VS, and it shows you:

  • Threads window — all running threads with their call stacks
  • Tasks window — C# Task objects and where they’re stuck (this is what caught the gRPC hang)
  • Managed Objects — the heap, what’s alive, what’s referencing what
  • Memory regions — raw memory view if you need it

In the gRPC case, I opened the dump in Visual Studio, went to the Threads window, and immediately saw tasks stuck on channel allocation. The call stack showed exactly which gRPC code was waiting and why.

If you want command-line analysis or don’t have VS, use:

  • dotnet-dump — CLI tool, looks at task stacks, heap, GC state
  • WinDbg — more powerful but steeper learning curve

I’ve written a post on WinDbg setup if you want to go deeper.

.NET Core Dumps: Linux (with lldb + SOS)

If your .NET app is running on Linux (which it probably is if you’re containerized), you’ll get core dumps, not minidumps. Analyzing them is different:

Capturing:

1
dotnet-dump collect -p <pid>  # or let coredump handlers write to disk

Analyzing with lldb + SOS:

1
2
3
4
5
lldb ./myapp /path/to/coredump
(lldb) plugin load /usr/share/dotnet/sos/sos.so
(lldb) clrstack # see managed call stacks
(lldb) dumpheap # analyze heap
(lldb) dumpobj # inspect specific objects

The SOS extension (Sequence Object Syntax) is your interface to .NET runtime state inside lldb. clrstack shows you managed code stacks (not just native stacks), which is critical—otherwise you’re staring at P/Invoke frames and nothing useful.

For the gRPC hang on Linux, clrstack on all threads would have shown the same picture: tasks stuck waiting for channel allocation.

Core Dumps: Other Platforms

  • Java: jcmd captures heap dumps; analyze with Eclipse Memory Analyzer or YourKit
  • JavaScript/Node.js: heap snapshots with Chrome DevTools or --inspect flag
  • Python: memory profilers or tools like Py-Spy

The specific tool varies, but the principle is the same everywhere: you’re looking at task/thread stacks to see where execution is stuck, and at the heap to see what objects are alive and using memory.

Capturing Dumps in Production

Here’s the thing: when you need a dump, you need it now. You can’t wait for deployment or special builds. The tools need to be in place before the problem happens.

For .NET:

  • dotnet-dump — capture dumps from running .NET processes, works in containers
  • Include it in your Docker image or deployment process

For Node.js:

  • heapdump npm module — can be triggered programmatically or via signals
  • Chrome DevTools — if you’re running with –inspect flag

For Java:

  • jcmd and jmap — built into the JDK, always available
  • async-profiler — can capture dumps and performance data

The key: deploy with dump-capturing capability already in place. In that gRPC situation, I could capture the dump immediately because dotnet-dump was already available. If it wasn’t, I would have had to reproduce the issue, deploy new tooling, and try again. That’s hours of wasted time.

If you’re running in containers (which you probably are), make sure your container image includes the diagnostic tools. A few extra MB in your image is nothing compared to the time you’ll save when you need a dump at 2am.

Why This Matters

That gRPC hang would have cost days of developer time without the dump. Trial and error, disabling features, hoping to stumble on the answer. Instead, one dump file gave us the truth in 30 seconds.

Memory dumps are your last resort when everything else fails. No logs to tell you what went wrong. No metrics to show you the symptom. Just the raw state of the application frozen in time.

In production-ready systems, memory dumps aren’t optional. They’re table stakes. Because sooner or later, you’ll hit a problem that only a dump can solve.

Series Roadmap

What’s Next?

Next, we’ll explore comprehensive testing—and why a false sense of security is worse than no tests at all.