Learn what memory leak is and see a representative example.
- [Instructor] So we call allocated memory that we can no longer access, because there's no pointer to it that we have, leak memory. Some programs are written so that by the time they finish, they have freed up all the memory that they dynamically allocated. Some programs don't do that. Some of the memory leak checking programs, run your program, and at the end they report any dynamically allocated memory that was not freed.
That might not really be a leak, but it's something a little more straightforward to do. So it is better if you have the practice in your code to always free memory after you don't need it anymore, so that when the program finishes, if there is any still dynamically allocated memory, then that is a mistake and the tools can help you find that. There's also some tools that expect that the memory will be freed within a certain amount of time, like within two seconds after allocating it, you're going to free it.
And if you haven't freed it, then it calls that a leak. So that can catch some extra cases. But you can see that that might not be a bug, you might need to keep that allocated memory around for, say, the whole time your program runs. Some programming languages use garbage collection so that it knows that memory is no longer accessible and it reclaims it. A lot of programs on Linux are written in C, some C++, and those languages don't have garbage collection so they are prone to these memory leaks.
Memory leaks can be a big deal. They can really slow down your system. So here we have a little C program, and it's going to loop 30 times, and in the loop it's going to call malloc, allocating 100 megabytes each time. And then we're going to loop, skipping one page at a time, changing one byte in each page, to make sure that those pages actually get allocated.
If we don't change anything in what we malloc, we don't really get it allocated. And it'll probably be in RAM, as long as there's RAM space, but it's not guaranteed to be there. And then we'll sleep for a second each iteration, to make this run a little bit longer. Let's run it, and while it's running, let's look at its size and its resident set size in the status proc file. So we'll run it in the background, and we'll get its pid that way.
And then with its pid, we can look at the proc status file. So there we see the Vm, virtual memory size and resident set size, and they're just about the same. The size can be bigger than the resident set size. And we see they keep getting bigger, until finally the program ended.
If we ran that a long time, those numbers could explode. Let's see what happens if we use perf. So we can run the program perf record, and we'll get information about where it's spending time. There we go. And we can look at the report with perf report. And we see over 80% of the time when that program was running, it was running in the kernel, running a function called clear_page_c_e.
When the kernal allocates a page for a process, it needs to zero it all out, because no telling what was in that page. Maybe there were cryptographic keys or something, and that takes some time. So that run time was really dominated by that allocating of the pages in the kernal. We get out of perf report just by typing q. Let's run pmchart, and we'll look at memory usage, while that memleak program is running.
So we do pmchart open view here. We're going to choose Memory. And we're going to get a graph here of memory usage. And we got the nice green lot free. And we're going to run memleak. And then we're going to bring the chart back up so we can see it. And while it's running, we can see the orange growing, that we got some sort of other memory allocated, it's not cache or buffer memory.
If you see memory continuing to grow, then that's something to investigate. And notice overall memory is 8 gig, we got up to about 4 gig, or so, and then it fell off quickly when the program ended. Let's look at one more tool with memleak. Let's compile it with debugging information, because then valgrind, the tool we'll look at, can give us line numbers.
So we're going to run our program with valgrind using the valgrind tool leak-check. Now it's going to take a little longer to run. So we get a report. And you can figure some of it out. There were 30 allocations, we looped 30 times, that sounds good, and zero frees, that's sounds bad. And then it says we leaked lots of bytes in nine blocks, on line 14 of memleak, and then we leaked a lot more in 21 blocks that were definitely lost on line 14.
So it was a little bit confused about the possibly and definitely, but we know they were definitely, because we didn't ever free them or reuse them. So we got the line number, memleak.c:14. If we compile this without the debugging option, we wouldn't get the line number, but we would get that address. So if you have a program you think is leaking and you don't have the source, you can't compile it or whatever, you can still run it with valgrind and get the report on possibly lost or definitely lost blocks, and then an address.
And with the address, with a debugger, you can probably find out the line.
- Timing techniques
- Packages for performance
- Identifying and optimizing CPU bottlenecks
- Finding memory bottlenecks
- Diagnosing disk bottlenecks