Detecting and Eliminating Memory Leaks | C++ Programming

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 →

Earn 3 college credits for C++ ProgrammingRecommended for credit by NCCRS · Computer Science 242 · self-paced · one payment, lifetime access

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.

Why take it here

Why students take C++ Programming with UPI Study

Your first real programming languageC++ teaches how programs really work: variables, control flow, functions, memory and objects. It is the base for data structures, algorithms, systems and game programming, and for most computer-science degrees.
Credit at a fraction of the priceThe same 3 credits can cost $750 or more at a US college. Here it is one payment, self-paced, with lifetime access.
Finish your degree soonerTransfer credits reduce the courses you still owe, so you can graduate earlier or lighten a heavy semester.
Strengthen an applicationA completed college-level course on an official transcript shows admissions and scholarship committees you can do the work.
Study on your own scheduleNo start dates, no deadlines. Watch the lessons, practice, and take the final when you are ready.
130,000+students helped with college creditsACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

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.

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?
A leak produces no error, warning, or wrong answer, so tests pass. The program holds a little more memory each time the leaking path runs, and the problem appears only when the accumulated total becomes large. That can be after hours of running, after many transactions, or on a customer's machine instead of yours.
What are the four common ways a C++ program leaks memory?
The four leak paths are a lost pointer, an early return, an exception path, and a destructor that does not clean up. Each one obtains memory and then loses the only means of releasing it. A lost pointer is given a new block while still holding an unreleased one, so the first address is overwritten.
Why does copying an object that owns a pointer cause a double release in C++?
The default copy operations duplicate the pointer, not the block it points to. After a copy constructor or copy assignment, two objects believe they own one block, and each destructor releases it, which is undefined behavior. The fix is to write both copy operations as deep copies, or forbid them, so each owner has its own block.
How do I find and prevent memory leaks in C++?
Count blocks obtained and released, run a leak checker, and tie release to object lifetime. A nonzero count at the end signals a leak. Valgrind and Address Sanitizer also find leaks and report where the memory was obtained. To prevent them, give each block an owner whose destructor releases it on every exit path.

About the C++ Programming course

How many college credits is C++ Programming?
C++ Programming (Computer Science 242) carries a NCCRS credit recommendation for 3 semester credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
How much does C++ Programming cost and what is included?
$250, paid once, with lifetime access: all 84 video lessons, a practice quiz after each, the graded work and final exam, and an official transcript when you finish. Students who want several courses choose UPI Study PRO instead.
Which universities accept UPI Study credits?
UPI Study has signed partner agreements with universities such as Thomas Edison State University, University of the People and Charter Oak State College, and works with universities such as Penn State, SNHU and SUNY. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities. Use "Find my college" on this page to check yours.
How long does C++ Programming take?
It is self-paced with no deadlines. The 84 lessons total about 0 hours of video; most students add reading and practice time and finish in a few weeks to a few months, on their own schedule.
How are the exams done?
Quizzes and assignments are online in the course. The final exam is taken online with proctoring; a person on the UPI Study team checks assignments, usually within two days.
How do my credits reach my college?
When the course shows 100% complete, request the official transcript from your dashboard. It is sent to the college you name, and you can send it to more than one. Check your college first with "Find my college" on this page, or ask Athena.

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.

C++ Programming
3 credits · NCCRS
Enroll →