Computer Memory, a concept that is often misunderstood. Some developers avoid certain programming languages altogether, in order to avoid having to deal with memory (we’ll explain what this means later). Others love the thrill of the chase, even when it takes 4 hours of valgrind-ing to find all the memory leaks! Memory is at the absolute core of digital technology, and our devices and smart gadgets would be nothing without it.
There are 3 things I want to teach you about computer memory today:
- Memory is accessed through addresses
- Memory is subject to leaks and errors
- Memory can be recycled and reused efficiently
In this article, we’ll be talking about Random-Access Memory (RAM), also called “volatile memory”. Not to be confused with the kind of memory you’d find in a hard drive or a solid-state drive, sometimes called “persistent memory”. At the most fundamental level, both types of memory offer the storage and retrieval of bits (read Bits and Bytes ). At a practical level, however, there is a huge difference between persistent and volatile memory. We’ll cover persistent memory in a later article, but for now, let’s talk about the 3 things you should know about volatile memory.
Memory is accessed through addresses
If you’ve ever installed RAM into a system, you might have looked at the thin black rectangles on the surface of the chip and wondered what they do or how they work. A very simplified version is as follows:
Each of those black rectangles contains millions or billions of transistors that can each be set to either 0 or 1. In order to access those individual bits, memory is broken down into chunks and are accessed by using addresses. These addresses are typically written in hexadecimal notation to make them more human-readable. A memory address is just like a house address in that it allows the memory to be located based on a logical organization. If you happen to have the address of a certain piece of data, you can use that to look up the data, write to it, or delete it altogether.
Since this isn’t a course on Electrical Engineering, we’ll avoid diving deep into how memory works. The important take away is that memory can be directly accessed through its address. This ability gives one immense flexibility, but it can also come with serious consequences. If you happen to access memory directly, without the guidance of the operating system (OS) or a “safe” programming style, you could end up with serious bugs (Note: as some readers have pointed out, virtual memory prevents a user from ever directly accessing memory. I am not getting into virtual memory in this article, but please note that it is, for most intents and purposes, not possible to directly access memory). Even worse, you could end up corrupting your user’s data, which is definitely not something you want to do. Memory leaks are a serious cause of frustration for Devs and are the subject of our next section.
Memory is subject to leaks and errors
When you store data in memory, you’re instructing the OS to keep track of its address so that you can reference it later. When you finally decide to access that data, the OS will look up and return its address so your app knows where to find it. Several programming languages handle this for you, so you don’t have to worry about memory addresses at all. This is a serious benefit to doing things like web development or programs that don’t need to be as efficient as possible. Even programming languages that do allow you to directly access memory contain “safe” ways of doing it, through the use of smart pointers and references.
There are at least 2 potential sources of memory issues:
- When you try to directly access memory that you shouldn’t
- When you try to access memory that does not contain actual data
Scenario #1: This doesn’t even require you to use deliberate memory addresses, it’s actually as simple as trying to access data in an array using an index that’s larger than the size of the array. By accessing memory that may not belong to your program, you might be corrupting data that belongs to other apps (or worse, to the OS). Of course, not all programming languages will allow this, and they’re smart enough to catch your mistake, but do you really want to rely on that? What’s worse, if you’re sharing a code base with other developers, this type of sloppy code will earn you a bad reputation. It’s a much better strategy to always use safe methods of accessing data.
Scenario #2: Let’s say you’re working in Swift, and you’re trying to access an optional piece of data. Optional’s are a type of safety net that is meant to prevent you from falling into scenario #2 in the first place. Best practice in Swift says that when you have an optional value, you always check to make sure there’s data available before you access it. But for our argument, let’s say you go against best practice and you try to access data in an optional that simply does not exist. What will happen? Your app will crash!
By accessing memory that does not exist, your app crashes, and your users are furious! Again, not all programming languages work exactly like this example, but most have similar constructs that allow naive devs to make similar mistakes. The takeaway here is that you have to be aware of where your data lives, how you’re accessing it, whether or not you ‘own’ it, and how to wrap data in safe code constructs. If you manage to avoid this pitfall, there’s another potential liability waiting just around the corner. What happens when your app builds up huge amounts of data in memory? Well, if it’s not recycled and reused correctly, your app (or your user’s device) will crash!
Memory can be recycled and reused efficiently
There’s nothing stopping a naive Dev from building up huge amounts of data in memory (and sabotaging their own app in the process). This is an extreme example, and you should pretty much never use “numerical literals” to define conditions for a loop (use a size or length property instead). It does go to show, however, that devs can crash systems by being naive about the amounts of data they’re using. Fortunately, Computer Scientists, Algorithm Aficionados, and Programming Language Wizards have spent years trying to solve this problem.
Enter Garbage Collection.
When data is no longer needed, what do you do with it? Take a minute to actually think that through. If you’ve been coding for a while and never had to think about this, you’re most likely using a programming language that does it for you. Garbage Collection is the programming construct that keeps track of memory, discards it when it’s no longer needed, and reuses the memory for new data. There is an excellent post about Visualizing Garbage Collection written by Ken Fox. I couldn’t do the topic enough justice, so check out his post if you want to dive deeper.
Many programming languages use garbage collection as a standard feature, which is probably why you’ve never had to think about it before. There are several types of GC (mentioned in Ken’s post), and each has their own pros and cons. Some things you should consider include:
- Does my chosen programming language have GC built in?
- If so, which type of GC is it, and is there anything I need to do?
- If not, how do I implement it myself?
If you’ve chosen a programming language that does not have GC built in, you’re in for a wild ride, but you’ll be a strong dev for having experienced it. Often times, custom GC will involve reference counting, which literally counts how many times your data is being used/accessed. When the count gets down to 0, you deallocate the memory and leave the rest to the OS. If you’re part of the lucky crowd and you don’t have to worry about GC in your given programming language, I would still advise you to read Ken’s post and be familiar with the topic, as it may come up in future career opportunities or in technical discussions.
A big part of being a software developer and advancing in your career is about having knowledge of topics that you might not necessarily need to know. This shows employers and colleagues your ability to get technical and research topics for the sake of understanding and being able to solve practical programming problems. Understanding these three concepts will get you on your way to being a stronger dev. You’ll be able to utilize memory more efficiently, plus you’ll increase the reliability of your apps and avoid causing yourself (and your users) some massive headaches down the road.