UPI Study › Courses › C++ Programming › Free lessons › Lesson 10.8 Free with a free account
Detecting and Eliminating Memory Leaks
C++ Programming · Chapter 10: File Access and MemorySection 10.8 · lesson 76 of 84video coming soon
← Dynamic AllocationNext lesson: Memory Lifecycle Drills →
In this lesson
Hunting silent failures.
- Explain why a leak shows no symptom until long after
- Name the 4 leak paths and the 4 release faults
- Prevent double release from copying an owner
- Count, use a leak checker, and tie release to lifetime
Transcript
Everything said in the video, section by section (1,695 words). Read along, or search it.
Opening Problem
Faults That Raise No Alarm
A leak produces no error, no warning, and no wrong answer. The program runs, passes its tests, and is signed off. Then it fails after eleven hours in service, somewhere unrelated to the fault. This section covers where leaks come from, and the related faults when memory is released wrongly. Then, how to detect all of them deliberately, and the structural habit that removes the whole category.
Definition
What a Memory Leak Really Is
A leak is memory that was obtained, and never given back while the program still runs. Nothing is corrupted, and no value is wrong. The program behaves correctly in every observable way. It simply holds more than it needs, and a little more each time the leaking path runs. That's what makes leaks a distinct kind of problem. Most faults announce themselves the first time the faulty line executes. A leak announces itself only when the accumulated total becomes a problem. After hours of running, or a hundred thousand transactions, or on the customer's machine rather than yours.
Side-by-Side Example
Two Functions, One Borrowing Forever
Here are two functions doing the same work. The correct one takes a block and gives it back, so its count stays level. The leaking one climbs, one block every pass. From outside, through the stretch where testing happens, the two behave identically. Only a counter like this tells them apart, and only much later does the leaking one run out. Both functions do the same work; only one gives back what it borrowed.
Leak Causes
Four Ways Memory Goes Missing
There are four common ways memory goes missing. Each one obtains memory, then loses the only means of releasing it. A lost pointer. P is given a new block, then given another, and the first block's only address is overwritten. An early return. The release sits at the bottom, so every path that leaves before reaching it skips it. An exception path. Control leaves the function without reaching the release, just like an early return, but with no visible line to blame. And a destructor that doesn't clean up. Each object obtains a block when created, and gives nothing back when destroyed.
Diagram
The Overwritten Pointer Pictured
Here's the lost pointer in picture form. Before, p refers to the first block. After the second assignment, p refers to the second block. The first block is still held by the program, but nothing can refer to it any more, so it can never be released. No line is individually mistaken. The fault is that the pointer was reused before its block was given back.
Related Faults
When Memory Is Released Wrongly
Leaks are a failure to release. The opposite mistakes are more dangerous, because they can corrupt data, not just consume it. A dangling pointer is one whose block has been released, but which still holds the old address. There's no symptom until it's used. Using it is a use after free. You get plausible rubbish, or a crash somewhere unrelated. A double release frees the same block twice, often causing a crash much later. And a mismatched release frees an array as a single item, or the reverse, giving silent corruption, or a crash.
Quick Fix
Assign nullptr After Releasing
One line removes three of those four faults. After releasing a block, assign nullptr to the pointer. A later use then fails immediately and obviously, instead of reading a block that's been handed to something else. And a second release becomes harmless, because releasing a null pointer is defined to do nothing at all. It costs one statement, and converts silent corruption into an immediate stop. For the fourth fault, match the forms exactly: array in, array out.
Common Mistake
Default Copy Duplicates the Address
Here's the trap that connects to copying. This buffer class holds a pointer member, and its destructor releases the block correctly. But when b is made as a copy of a, the default copy duplicates the address, not the block it refers to. Now two objects believe they own the same memory. Both are destroyed at the closing brace, and each destructor releases the block. So it's released twice. That is undefined, so this program is shown, never run.
Diagram
Shallow Copy Versus Deep Copy
A shallow copy duplicates the arrow, not what it points at. On the left, a's data and b's data both point at one block, which will be released twice. On the right is the deep copy you must write. Each object gets its own block, holding the same contents.
Repair
Writing Copy Operations for Owners
The repair is the copy operations covered earlier, written so that each object gets its own block. Here, the copy constructor allocates and copies. Run it: the two objects hold different blocks, and the program counts two blocks live, one per owner. The rule of thumb: a type that needs a destructor almost always needs its copy operations written too. If you've written one and not the others, look there first.
Diagnosis Clues
Symptoms Far From the Cause
With memory faults, the symptom is never where the fault is. A crash in code that hasn't changed means something damaged memory earlier. The crashing line is only the first to notice. Fine in short runs but failing in long ones points to a leak, on a path that runs repeatedly. Failing only in the release build, or only after an unrelated edit, means memory is arranged differently, and existing damage now lands somewhere that matters. And values changing with nothing assigning them means a write through a stale pointer. The location of the symptom says almost nothing about the location of the cause. That's why guessing doesn't work here.
Detection Method
Counting Obtains and Releases by Hand
The cheapest detector needs no tools at all. Keep a count of blocks obtained and blocks released, and report the difference before the program ends. Every allocation goes through obtain, which adds one. Every release goes through release, which subtracts one. A nonzero total at the end is a leak. It's crude, but it answers the first question in a minute, on any machine. Run the suspect operation a thousand times and watch the number. Steady means no leak on that path. Climbing means one, and the rate tells you how many blocks each pass loses. Here, one thousand passes leave one thousand blocks held.
Tools
Valgrind, Address Sanitizer, and IDE Panels
Proper detectors watch every allocation the program makes, including inside library code you didn't write. Valgrind needs no rebuild. It slows the program considerably, which is fine for a test run. Address Sanitizer is built into the compiler. Rebuild once with its flag, and run normally. It's much faster, and also catches writes past the end of a block. And development environments usually have a diagnostics panel, which shows memory growing over time, so you can spot the climb before hunting the cause.
Reading Output
Interpreting a Valgrind Leak Report
Here's what a leak report looks like: a sample from Valgrind, on Linux. Read the summary first. Four hundred bytes in one block are definitely lost. Definitely lost means nothing in the program can still reach that block. It's a real leak. Beneath that is the sequence of calls that obtained it, most recent first. Look for the line in your own file. Here, that's line twelve of buffer dot c p p. The tool tells you where the memory was obtained, never where the release should have gone. That line doesn't exist, because the release was never written. Deciding where it belongs is your part.
Root Cause
Release as a Step Gets Skipped
Every cause in that first listing is the same mistake in different clothing. A release written as a step, which some path then skips. A function can be left in three ways: an early return, an exception, or the normal end. A release written as a step sits on just one of those paths. The fix isn't being more careful about writing that step. It's to stop writing it at all. Make the block the property of an object, so releasing happens automatically when that object is destroyed, on every path.
Worked Example
Ownership Written Once in a Class
Here's ownership, written once. The buffer obtains its block when it's created, and releases it when it's destroyed. Its copy operations are written too, here simply forbidden. Now in work, if something causes an early return, the block is still released, because b is destroyed there. If it throws, it's still released. And on the ordinary path, it's released at the closing brace. The output shows three obtained and three released. Most of the time, use a type that already does this. Collections, strings, and managing pointers own and release their storage for you. Write your own owner only when you must. When you do, this pattern is the whole of it.
Best Practices
Habits That Prevent Memory Faults
A few habits prevent all of this. Prefer a type that owns its storage. Where you do manage a block, give it an owner immediately. Set a pointer to nullptr the moment its block is released, and match the release form to the allocation. If a type needs a destructor, assume it needs its copy operations written too. Run one leak-checked build before anything is considered finished, not after a report arrives. And when hunting, count first. Knowing whether the number climbs is worth more than any guess about where.
Key Terms
Vocabulary of Memory Faults
A lost pointer is a block made unreachable, because its only address was overwritten. A dangling pointer still holds the address of a released block, and using it is a use after free. A double release frees the same block twice. A mismatched release frees it in the wrong form. A shallow copy duplicates a pointer member. A deep copy gives the new object its own block, with the same contents. And ownership is the rule that exactly one object is responsible for releasing a given block.
Quick Check
Does Nulling Fix the Leak?
Quick check. In a function, a call that may throw sits between new and delete. A colleague adds p equals nullptr after the delete. Does that fix the leak? Pause and think. No. When the call throws, control leaves before the delete, and before the added line too, so the block still leaks. Nulling only guards against a later use, or a second release. The fix is an owner, whose destructor runs on every path out.
Key Takeaways
Leaks Summarized: Detect and Prevent
To summarize. A leak shows no symptom until the accumulated total becomes a problem, long after any test. Lost pointers, early returns, exceptions, and destructors that don't clean up all leak. Null the pointer after release, and match the forms. Copying an owner needs written copy operations. Count, run a leak checker, and tie release to lifetime.
About C++ Programming at UPI Study
C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.
Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Enroll now → Course details Download the syllabus Every course with PRO →
Why take it here
Why students take C++ Programming with UPI Study
Where the credit goes
Universities that work with NCCRS
C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.
FAQ
Questions students ask us
About Detecting and Eliminating Memory Leaks
Why does a memory leak show no symptom until long after the code was written?
What are the four common ways a C++ program leaks memory?
Why does copying an object that owns a pointer cause a double release in C++?
How do I find and prevent memory leaks in C++?
About the C++ Programming course
How many college credits is C++ Programming?
How much does C++ Programming cost and what is included?
Which universities accept UPI Study credits?
How long does C++ Programming take?
How are the exams done?
How do my credits reach my college?
Ready for the full course? Earn 3 credits in C++ Programming.
84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.
3 credits · NCCRSEnroll →