How to find and fix a memory leak in a program (2026)
Quick Answer
To find and fix a memory leak, first confirm that memory use keeps growing during normal operation and does not fall back after work is finished. Then reproduce the problem in a controlled test, use the right diagnostic tool for your language or platform to identify what objects or allocations remain alive, and change the code so every allocation, subscription, handle, cache entry or background reference is released when it is no longer needed. If the leak appears in production only, is inside native code, or causes crashes you cannot safely analyse, bring in an experienced developer or performance engineer.
Overview
A memory leak happens when a program keeps memory, or memory-related resources, after they should have been released. In languages with manual memory management, that often means allocated memory is never freed. In garbage-collected languages, leaks still happen when objects remain reachable because something keeps a reference to them, such as a cache, static collection, event listener, goroutine, thread-local value or closure. Leaks can also involve resources that behave like memory problems in practice, including file handles, database cursors, sockets, graphics buffers and native library allocations. The practical way to fix a leak is to avoid guessing. Start by reproducing the growth reliably, then measure it with a profiler, leak detector or heap dump tool that matches your runtime. Compare snapshots before and after the leaking action, look for object counts or retained size that keeps increasing, and trace back the reference chain to the code that prevents cleanup. Fixes usually include releasing resources deterministically, removing stale references, limiting caches, unsubscribing listeners, correcting object ownership, and ensuring worker processes or background tasks finish cleanly. After the fix, rerun the same workload and confirm memory stabilises rather than merely growing more slowly.
Who this is for
Developers, SREs and technical support staff troubleshooting a desktop, mobile, server or embedded program that grows in memory use over time.
What you’ll need
- A repeatable way to trigger the suspected leak
- Access to logs, runtime metrics or operating system memory usage
- A profiler, heap analyser or leak detector appropriate to the language or platform
- The program's source code or a developer who can change it
- A test environment where you can run the workload safely
Before you start
Check that the problem is really a leak rather than expected cache growth, one-off startup allocation, fragmentation, large input data, or another process consuming memory. Record the language runtime, framework version, operating system and the exact action that causes growth. If the issue is in production, avoid attaching heavy tools blindly; use the lightest monitoring first and reproduce in staging if possible.
Step-by-step
- 1
Confirm that memory use is genuinely leaking
Run the program through the same action or workload several times and observe whether memory use keeps rising after each cycle. Give the program time to become idle between runs, especially for garbage-collected runtimes. Check both process memory at the operating system level and, where available, runtime heap metrics.
Why: Many false alarms come from temporary spikes, delayed garbage collection, warm-up behaviour or caches filling for the first time. You need a stable symptom before diagnosing the cause.
- 2
Reproduce the leak in a controlled test
Reduce the problem to the smallest reliable scenario: one request path, one screen, one batch job, one import, or one loop. Disable unrelated features where practical. If possible, create an automated test or script that repeats the leaking action and records memory before and after each iteration.
Why: A minimal reproduction shortens profiling time, makes object growth easier to spot, and lets you verify later that the fix really worked.
- 3
Use the right tool to identify growing allocations
Choose a tool that matches the stack. For native C or C++, use leak detectors and sanitisers. For Java, .NET, Python, JavaScript, Go and similar runtimes, use the platform's memory profiler, heap dump or built-in diagnostics. Take snapshots before the action and after repeated runs, then compare retained objects, allocation sites, and reference paths or retaining trees.
Why: You rarely find leaks reliably by reading code alone. Profiling shows what remains in memory, where it came from, and what is still keeping it alive.
- 4
Classify the leak by cause
Look for common patterns: memory allocated but never freed; objects held in global or long-lived collections; unbounded caches; event listeners or callbacks never removed; timers, threads or goroutines that never stop; file, socket or database resources not closed; closures capturing large objects; circular ownership in manual-memory code; and native allocations hidden behind wrappers.
Why: The fix depends on the ownership model. A lost pointer in C needs a different remedy from a retained reference in Java or a forgotten event subscription in JavaScript.
- 5
Apply the fix that matches the cause
For manual-memory code, ensure every allocation has a clear owner and is released on all code paths, including error handling; prefer RAII, smart pointers or equivalent ownership tools where available. For garbage-collected code, remove references when work is complete, unsubscribe listeners, clear stale collections, bound caches with eviction, cancel timers and background tasks, and avoid static or singleton state unless necessary. Always close files, sockets, streams, database cursors and similar resources using language features for deterministic cleanup. If native libraries are involved, verify their disposal API is called correctly.
Why: Memory stops growing only when the program no longer retains the object or resource beyond its useful lifetime.
- 6
Verify under the same workload
Rerun the same reproduction after the change. Compare memory trend, heap snapshots and resource counts with the original results. Keep testing long enough to prove memory stabilises after repeated cycles, not just that the growth is smaller. Add or update an automated regression test if your stack supports it.
Why: A partial fix can hide the symptom briefly while another retention path remains. Verification prevents repeated production incidents.
- 7
Harden the code to prevent recurrence
Add monitoring for memory and resource use, enforce code review around ownership and cleanup, and document lifetime rules for shared objects. Where possible, use safer abstractions such as context managers, using blocks, defer patterns, bounded caches and lifecycle hooks for startup and shutdown.
Why: Leaks often return when code evolves. Preventive patterns reduce the chance of reintroducing the same class of bug.
Why this works
Memory leaks persist because the program's ownership and lifetime rules are wrong: either allocated memory is never released, or something still references data so the runtime cannot reclaim it. Profiling, snapshot comparison and reference analysis expose those lifetime mistakes, letting you remove the retention path or add the missing cleanup.
Common mistakes to avoid
- Treating any rise in memory use as a leak without checking whether it later falls back
- Profiling only total process memory and never inspecting heap contents or retaining references
- Ignoring non-memory resources such as sockets, file handles and database cursors that create similar symptoms
- Clearing the symptom by restarting the process instead of fixing the code path
- Using weak cleanup conventions instead of deterministic disposal for resources that must be closed
- Testing the fix with a different workload than the one that revealed the leak
Troubleshooting
Memory rises during a task but drops later
This may be normal peak usage or delayed garbage collection rather than a leak. Repeat the task several times with idle periods and compare snapshots taken after the program settles.
Heap looks stable but process memory keeps growing
Check for native allocations, memory fragmentation, graphics buffers, memory-mapped files, or leaks in dependencies outside the managed heap. Use operating system and native-code tools, not only language-level profilers.
No obvious object type is growing
Look at retained size and reference chains, not just object count. A small number of root objects can keep large graphs alive.
Leak happens only in production under load
Collect lightweight metrics first, reproduce the traffic pattern in staging, and sample heap or allocation data carefully. If the service is business-critical, involve a performance specialist before attaching heavy profilers.
Fix works locally but not in long runs
Check for a second leak path, unbounded logging or metrics buffers, batch accumulation, or cleanup code that runs only on the success path and not on retries or exceptions.
Compare your options
Built-in runtime profiler or heap dump tools
Best for: Managed runtimes such as Java, .NET, Python, JavaScript and Go
Pros: Shows live objects, allocation sites and reference paths; usually well integrated with the platform
Cons: May miss native memory issues; can add overhead; interpretation takes practice
Native leak detectors and sanitisers
Best for: C, C++ and programs using native extensions or libraries
Pros: Good at finding allocations not freed and misuse of memory
Cons: Can slow execution considerably; setup may be more involved; not ideal on sensitive production systems
Operating system process monitoring
Best for: Initial confirmation and production-safe observation
Pros: Low risk and easy to start with; helps confirm whether the process is the source of growth
Cons: Does not explain which objects or code paths are responsible
| Option | Best for | Pros | Cons |
|---|---|---|---|
| Built-in runtime profiler or heap dump tools | Managed runtimes such as Java, .NET, Python, JavaScript and Go | Shows live objects, allocation sites and reference paths; usually well integrated with the platform | May miss native memory issues; can add overhead; interpretation takes practice |
| Native leak detectors and sanitisers | C, C++ and programs using native extensions or libraries | Good at finding allocations not freed and misuse of memory | Can slow execution considerably; setup may be more involved; not ideal on sensitive production systems |
| Operating system process monitoring | Initial confirmation and production-safe observation | Low risk and easy to start with; helps confirm whether the process is the source of growth | Does not explain which objects or code paths are responsible |
Alternatives
- If direct profiling is too risky in production, reproduce the workload in staging with the same runtime and configuration
- If the codebase is large, use binary search on features or request paths to isolate the leaking component before deep profiling
- If a third-party dependency appears responsible, update it to a supported version or replace it after confirming the issue with vendor documentation or issue trackers
Pro tips
- Take snapshots at consistent points, such as immediately after each test cycle, so comparisons are meaningful
- Focus on retained size, not only allocation count
- Check cleanup on exception and cancellation paths; leaks often hide there
- Bound every cache unless there is a very good reason not to
- Make object ownership explicit in code reviews, especially at module boundaries
- If wrappers call native libraries, verify both the wrapper object and the native handle are disposed correctly
Safety notes
- Do not attach heavy profilers to critical production systems without assessing performance risk and rollback options
- Avoid collecting heap dumps that may contain personal data or secrets unless you have an approved handling process
- Test leak fixes in a safe environment before deploying to systems where memory pressure could cause service failure
Legal & regulatory notes
Heap dumps, logs and traces may contain personal data, credentials or confidential business information. Handle them under your organisation's security and privacy rules and any applicable data protection law.
What this guide does not cover: This guide gives language-agnostic troubleshooting steps and does not provide tool-by-tool instructions for every programming language, framework or operating system.
Cost considerations
Ignoring a leak can increase infrastructure use, trigger restarts or outages, and consume engineering time later. Early diagnosis is usually cheaper than scaling hardware to mask the problem.
Prefer to hire a professional?
If this job is beyond a confident DIY — or the work needs to be done by a licensed professional — you can find a verified qualified local tradesperson through Trade Directory (tradedirectory.co.nz), New Zealand's directory of real, verified trade businesses. Compare local tradespeople, read their details and request quotes before you commit.
Find a qualified local tradesperson on Trade DirectoryFrequently asked questions
Can garbage-collected languages still have memory leaks?+
Yes. The runtime only collects objects that are no longer reachable. If your code keeps references in caches, globals, event listeners, closures or background tasks, those objects remain alive.
How do I tell a leak from a cache?+
A healthy cache should have a policy and a practical limit, and its size should stabilise under steady use. A leak usually keeps growing without a sensible bound or keeps objects longer than intended.
What if the leak is in a third-party library?+
Confirm it with profiling, check whether you are using the API correctly, then review the library's current documentation, release notes and issue tracker. Updating, reconfiguring or replacing the dependency may be the safest fix.
Is restarting the process an acceptable fix?+
Only as a temporary operational workaround. It may reduce impact, but it does not remove the underlying bug and can hide a serious resource management problem.
Why does memory not return fully to the operating system after a fix?+
Some runtimes and allocators keep memory reserved for reuse even when the active heap is lower. What matters first is whether live heap or retained objects stop growing under the same workload.
Sources & references
Guidance on this page is traced to documented sources. Last checked 24 September 2026.
- Microsoft Learn - Debug a memory leak in .NET · official
Supports the use of memory usage tools and snapshot comparison to identify objects and allocations responsible for growth in .NET applications.
- Oracle Java SE Troubleshooting Guide - Troubleshooting Memory Leaks · official
Supports the distinction between Java heap leaks and other memory issues, and the use of heap analysis and reference paths to locate retained objects.
- Google Sanitizers Wiki - AddressSanitizerLeakSanitizer · industry
Supports using sanitisers to detect leaked allocations in native code and the need for tooling beyond code inspection alone.
- Node.js Diagnostics - Memory · official
Supports diagnosis of memory growth in Node.js using heap and runtime diagnostics, and the idea that managed runtimes can still leak through retained references.
The core troubleshooting method changes slowly, but the best tools and exact workflows vary by language, runtime and IDE version.