Memory leak detection - How to find, eliminate, and avoid
Posted Jan 9, 2020 | 6 min. (1222 words)As a software developer, you might not be used to thinking about the memory usage of your applications. In today’s development world, memory is plentiful and usually quite fast. Odds are, the programming language you use doesn’t require you to allocate your own memory, or free it when you’re done. That doesn’t mean that you’re in the clear when it comes to memory leaks though.
The reality is that memory leaks can strike any application in any language. They’re more common in older or “closer to the metal” languages like C or C++, sure. But all it takes is a visit to one poorly-optimized web page to discover that even a language like JavaScript can have problems with memory leaks.
What is a memory leak?
A memory leak is any portion of an application which uses memory without eventually freeing it. By memory, we’re talking about RAM, not permanent storage, like a hard drive. Even on very high-end servers, RAM is a limited resource. An application which continually uses more memory without freeing any will eventually exhaust the server’s pool of memory. When this happens, the application is likely to crash the next time it attempts to use more memory.
In years past, when the plurality of developers wrote C and C++, memory leaks were a common occurrence. This is because it was up to developers to make sure that their application freed memory after they were finished using it. It was easy to forget to do that! Their application would slowly use up more and more memory, keeping long-outdated information in its registers. Eventually, the program would stop, and all that memory would be freed. Sometimes, that was on purpose. Other times, it was because the application crashed.
In the most common programming languages today, you can rely on the language run time to help free unused memory automatically. We’ll dive into just how that works in a little bit.
What do memory leaks cost me?
Most developers are well-insulated from the consequences of memory leaks in today’s world. Lots of long-running applications don’t run on someone’s desktop, or a server in a closet in your building. Instead, they’re running in the cloud, on a service like AWS. Or they’re a single-page application that runs in a user’s browser. Even in those environments, proper memory management is an important skill.
To drive home why, let me share a story from a previous job. I started there a few years after the organization was originally founded. Up to that point, all of the developers who’d worked on the software were pretty inexperienced. They’d managed to put together the application, but they failed to properly dispose of connections to outside servers when utilizing network calls. This code was written in C#—not at all a low-level language. The application we worked on needed to make a lot of network calls. Often times, several per customer per minute. Multiple times per day, we’d have to restart the entire application, in the middle of the workday, to fix the problem with reaching the limit of number of open connections.
This had a direct monetary cost in two ways. First off, the application ran on the largest possible Azure instance. The company was spending astonishing amounts of money every month on a comparatively tiny number of customers. Their goal was to scale to support 100 or 1000 times as many customers. Doing so was not remotely possible with that software. What’s more, we needed to restart the servers in the middle of the business day. Instead of growing our customer base, we had customers leaving because they couldn’t access critical software when they needed it.
How can I detect a memory leak?
The simplest way to detect a memory leak is also the way you’re most likely to find one: running out of memory. That’s also the worst way to discover a leak! Before you run out of memory and crash your application, you’re likely to notice your system slowing down. If you do, it can be time to start digging into your code to figure out just what’s using up all your RAM.
Often, you’ll do this using a profiling tool. Modern IDEs like Visual Studio have tools built in which will show you just how much memory is being used on which parts of your application. A profiling tool isn’t a silver bullet: it won’t tell you right away which parts of your application are leaking memory. What it will do is tell you which parts of your application are using the most memory. If you run them for an extended period of time, you can also see which parts of the application use more memory over time. Armed with this knowledge, you can narrow down the search in your application for leaky code.
Another method for memory leak detection is to use logging intelligently. Sometimes, faulty code doesn’t cause a memory leak, but your users do. Maybe a user has uploaded a very large file that they’re trying to access on your servers. If you’re loading that entire file into memory, you might exhaust the application’s memory through no fault of your own. Mature software organizations will often use automated tools to detect memory leaks in running applications.
How can I prevent a memory leak?
As we talked about earlier, lots of popular programming languages today include features to assist developers with automatic memory management. They implement a system called a garbage collector which frees up memory that the application doesn’t need. Garbage collectors are fairly complicated bits of code. If you’re trying to prevent a broad class of memory leaks, you’ll be well-served to spend time learning about your language’s garbage collection algorithms. There are a few different kinds of garbage collectors. Understanding how your application’s garbage collection works is an important step in preventing a wide variety of memory leaks.
Another important way to prevent memory leaks is to write code which disposes of unneeded resources. Nearly all languages include resource types which aren’t automatically freed. Examples include things like file handles and networked resource connections, like from our previous story. You need to write specific code that tells the application that the resource’s work has finished.
In the most extreme examples, you can use the brute force system of eliminating memory leaks: restarting the process. You should consider this a stopgap solution at best though.
Doing this is like a doctor cutting off your arm because you hit your thumb with a hammer. You can prevent the root of the problem by being more careful, and you won’t need such drastic measures.
Memory leaks don’t need to be a headache
Memory leaks usually sneak into application code because the developer writing the code doesn’t know any better. This can make them very difficult to find. Because the developer who knows the code best doesn’t know what’s causing the leak, it’s really difficult to figure out where it’s coming from.
If you’ve found a memory leak in your code, don’t view it as a failure of software. Instead, look at it as a chance to improve your craft and learn together as a team. As you grow in your skills, you’ll find that you naturally write code which protects your applications from memory leaks by using the skills we’ve talked about here.