Measuring memory usage in Python: it’s tricky! (2023)

If you want your program to use less memory, you will need to measure memory usage.You’ll want to measure the current usage, and then you’ll need to ensure it’s using less memory once you make some improvements.

It turns out, however, that measuring memory usage isn’t as straightforward as you’d think.Even with a highly simplified model of how memory works, different measurements are useful in different situations.

In this article you’ll learn:

  • A simplified but informative model of how memory works.
  • Two measures of memory–resident memory and allocated memory–and how to measure them in Python.
  • The tradeoffs between the two.

A (not so) simple example

Consider the following code:

>>> import numpy as np>>> arr = np.ones((1024, 1024, 1024, 3), dtype=np.uint8)

This creates an array of 3GB–gibibytes, specifically–filled with ones.Naively, once this code runs we expect the process to be using a little bit more than 3GB.

One way to measure memory is using “resident memory”, which we’ll define later in the article.We can get this information using the handy psutil library, checking the resident memory of the current process:

>>> import psutil>>> psutil.Process().memory_info().rss / (1024 * 1024)3083.734375

With this particular measurement, we’re using 3083MB, or 3.08GB, and the difference from the array size is no doubt the memory used by the Python interpreter and the libraries we’ve imported.Success!

But resident memory is tricker than it seems.Let’s say I go and open some websites in my browser, leaving the Python interpreter running the background.Then I switch back to the interpreter, and run the exact same command again:

>>> psutil.Process().memory_info().rss / (1024 * 1024)2902.12109375

What’s going on?Why did 200MB of memory disappear?

To find the answer we’ll need to learn a bit more about how the operating system manages memory.

A simplified model of memory allocation

A running program allocates some memory, i.e. gets back an address in virtual memory from the operating system.Virtual memory is a process-specific address space, essentially numbers from 0 to 264-1, where the process can read or write bytes.

In a C program you might use APIs like malloc() or mmap() to do so; in Python you just create objects, and the Python interpreter will call malloc() or mmap() when necessary.The process can then read or write to that particular address and consecutive bytes.

We can see this in action by using the Linux utility ltrace to trace calls to malloc().Given the following program:

import numpy as nparr = np.ones((170_000,), dtype=np.uint8)

We can run Python under ltrace:

$ ltrace -e malloc python ones.py..._multiarray_umath.cpython-39-x86_64-linux-gnu.so->malloc(170000) = 0x5638862a45e0...

Here’s what happening:

  1. Python create a NumPy array.
  2. Under the hood NumPy calls malloc().
  3. The result of that malloc() is an address in memory: 0x5638862a45e0.
  4. The C code used to implement NumPy can then read and write to that address and the next consecutive 169,999 addresses, each address representing one byte in virtual memory.

Where are these 170,000 bytes stored?

  • They can be stored in RAM; this is the default.
  • They can be stored on the computer’s hard drive or disk, in which case they’re said to be stored in swap.
  • Some of the bytes might be stored in RAM and some in swap.
  • Finally, they can be stored nowhere at all.

For now, we’ll ignore that confusing last case, and just focus on the first three situations.

Resident memory is RAM usage

RAM is fast, and your hard drive is slow… but RAM is also expensive, so typically you’ll have far more hard drive space than RAM.For example, the computer I’m writing this on has about 500GB of hard drive storage, but only 16GB RAM.

Ideally, all of your program’s memory would be stored in fast RAM, but the various processes running on your computer might have allocated more memory than is available in RAM.If that happens, the operating system will move–or “swap”–some data from RAM to hard drive.In particular, it will try to swap data that isn’t actively being used.

And now we’re ready to define our first measure of memory usage: resident memory.Resident memory is how much of the process’ allocated memory is resident–or stored–in RAM.

In our first example we started out with all 3GB the allocated array stored in RAM.

3GB array (=allocated memory)
3GB in RAM (=resident memory)

Then when I opened a bunch of browser tabs, loading those websites used quite a lot of RAM, and so the operating system decided to swap some data from RAM to disk.And part of the memory that got swapped was from our array.As a result, the resident memory for our Python process went down: the data was all still accessible, but some of it had been moved to disk.

3GB array (=allocated memory)
2.8GB in RAM (=resident memory)0.2GB on disk

An alternative: allocated memory

It would be useful to measure allocated memory, to always get 3GB back regardless of whether the operating system put the data in RAM or swapped it to disk.This would give us consistent results, and also tell us how much memory the program actually asked for.

In Python (if you’re on Linux or macOS), you can measure allocated memory using the Fil memory profiler, which specifically measures peak allocated memory.The Sciagraph profiler is another alternative: unlike Fil, it also does performance profiling, and it’s lower overhead by using sampling.

Here’s the output of Fil for our example allocation of 3GB:

The tradeoffs between resident memory and allocated memory

As a method of measuring memory, resident memory has some problems:

  • Other processes can distort the results by using up limited RAM and encouraging swapping, as they did in our 3GB example above.
  • Because resident memory is capped at available physical RAM, you never actually learn how much memory the program asks for, once you hit that ceiling.If you have 16GB of RAM you won’t be able to distinguish between a program that needs 17GB memory and a program that needs 30GB of memory: they will both report the same resident memory.

Allocated memory, on the other hand, is not affected by other processes, and tells you what the program actually requested.

Of course, resident memory does have some advantages over allocated memory:

  • That swapped memory may well never be used: imagine creating an array, forgetting to delete the reference, and then never actually using it again for the rest of the program.If it gets swapped, that’s fine, and arguably we shouldn’t be measuring it.
  • More broadly, because resident memory measures actually used RAM from the operating system perspective, it can capture edge cases that aren’t visible to the allocated memory tracking.

Let’s look at an example of one such edge case.

Phantom memory!

In all our examples so far we’ve been allocating arrays that are full of ones.If we’re measuring allocated memory, what the array is filled with makes no difference: we can switch to creating arrays full of zeroes, and still get the exact same result.

But on Linux, resident memory tells us an interesting story:

>>> import numpy as np>>> import psutil>>> arr = np.zeros((1024, 1024, 1024, 3), dtype=np.uint8)>>> psutil.Process().memory_info().rss / (1024 * 1024)28.5546875

Once again, we’ve allocated a 3GB array, this time full of zeroes.We measure resident memory and–the array isn’t counted, resident memory is just 29M.Where did the array go?

It turns out that Linux doesn’t bother storing all those zeroes in RAM.Instead, it will add chunks of zeroes to RAM only when the data is actually accessed–until then no RAM is used.Until that happens, allocated memory will report 3GB, but resident memory will notice that those 3GB aren’t actually using any resources.

Allocated memory is a good starting point

It’s worth keeping in mind that this is still a quite simplified model of memory usage.It doesn’t cover the file cache, or memory fragmentation in the allocator, or other available metrics–some useful, some less so–you can get from Linux.

That being said, for many applications allocated memory is probably sufficient as a first-pass measure to help you optimize your program’s memory use.Allocated memory tells you what your application actually asked for, and that’s usually what you’re going to have to optimize.

FAQs

How does Python measure memory usage? ›

You can use it by putting the @profile decorator around any function or method and running python -m memory_profiler myscript. You'll see line-by-line memory usage once your script exits.

Why Python is not memory efficient? ›

It is a slower way of memory allocation. Once static memory is allocated, neither its size can be changed, nor it can be re-used. Hence, less efficient. We can change the memory size after allocation and can be reused as well.

How do I optimize memory usage in Python? ›

The most used file is the arr object which takes up 2 memory blocks with a total size of 2637 MiB.
...
  1. Utilize Pytorch DataLoader. ...
  2. Optimized data type. ...
  3. Avoid using global variables, instead utilize local objects. ...
  4. Use yield keyword. ...
  5. Built-in Optimizing methods of Python. ...
  6. Import Statement Overhead. ...
  7. Data chunk.
Aug 31, 2021

Why is Python using so much memory? ›

Those numbers can easily fit in a 64-bit integer, so one would hope Python would store those million integers in no more than ~8MB: a million 8-byte objects. In fact, Python uses more like 35MB of RAM to store these numbers. Why? Because Python integers are objects, and objects have a lot of memory overhead.

How does Python handle memory allocation? ›

Memory management in Python involves the management of a private heap. A private heap is a portion of memory that is exclusive to the Python process. All Python objects and data structures are stored in the private heap. The operating system cannot allocate this piece of memory to another process.

How is memory usage measured? ›

Check Computer Memory Usage Easily

To open up Resource Monitor, press Windows Key + R and type resmon into the search box. Resource Monitor will tell you exactly how much RAM is being used, what is using it, and allow you to sort the list of apps using it by several different categories.

What are disadvantages of memory management in Python? ›

Memory Consumption: Another disadvantage of Python is its large memory consumption compared to other programming languages (again C or C++). Python data types are flexible which incurs some higher memory overhead. This excellent article gives you everything you need to understand memory management in Python.

Why does Python have poor performance? ›

Unlike other popular programming languages including C# or JAVA, Python is dynamically typed and an interpreted language. It is slow primarily due to its dynamic nature and versatility.

How do I avoid memory errors in Python? ›

The unexpected memory error can be solved if you install the 64bit version of Python instead of the 32-bit version because it will have much more storage and RAM even though there is more memory usage. A dataset error should use the dedicated generator methods and classes that will be discussed.

Does Python clean up memory? ›

The memory Heap in Python holds the objects and other data structures used in the program. So, when a variable (a reference to an object) is no longer in use, the Python memory manager frees up the space, i.e. it removes the unnecessary object.

Can you dynamically allocate memory in Python? ›

Dynamic memory allocation is mostly a non-issue in Python. Everything is an object, and the reference counting system and garbage collector automatically return memory to the system when it is no longer being used.

How do I reduce memory usage in code? ›

Use bit fields rather than ints to store flags and small integers. Avoid using fixed length character arrays to store strings, implement a string pool and use pointers. Where storing references to an enumerated string list, e.g. font name, store an index into the list rather than the string.

What are the two types of memory management in Python? ›

There are two types of memory allocation in Python, static and dynamic.
  • Static memory. The stack data structure provides static memory allocation, meaning the variables are in the stack memory. ...
  • Dynamic memory.

Does Python use stack or heap? ›

Memory allocation in Python

The function calls and the references are stored in the stack memory whereas all the value objects are stored in the heap memory.

How much memory can Python allocate? ›

Python doesn't limit memory usage on your program. It will allocate as much memory as your program needs until your computer is out of memory. The most you can do is reduce the limit to a fixed upper cap. That can be done with the resource module, but it isn't what you're looking for.

Why is memory usage at 100%? ›

Why is my memory usage so high in Windows 10? One reason could be a big program or game that takes high system RAM. The other reason could be malware that caused your device high memory usage.

How is memory size calculated? ›

Solution:
  1. (File Size) X 20 = (Amount of RAM needed to load the file)
  2. (Amount of RAM on your computer) / 20 = (The maximum size of file you can load on your computer) – so if you have 64GB of RAM you can go up to 3.2GB in file size.
May 19, 2022

Is memory usage the same as RAM? ›

Memory and storage are also not the same thing, even though the words are often used interchangeably. Memory is another term for RAM.

What is the problem of memory management? ›

Memory management problems. The basic problem in managing memory is knowing when to keep the data it contains, and when to throw it away so that the memory can be reused. This sounds easy, but is, in fact, such a hard problem that it is an entire field of study in its own right.

What is one major drawback limitation of using Python? ›

Slow Speed

After the high memory usage, it's lack of speed is one of the biggest disadvantages of Python. As it executes the code one line at a time, the speed of execution often is hampered. Where speed is important for the project, Python cannot be used for coding.

What was the weakness of Python? ›

Weak in Programming for Mobile Devices

This is because Python has the slow processing power and is hardly memory efficient when compared to other programming languages.

Why is Python so popular despite being so slow? ›

In this article, we will see the reasons Why is Python so popular despite being so slow. Python is a high-level, object-oriented, dynamic, and multipurpose programming language i.e multi-paradigm language. Python's syntax, dynamic typing, and interpreted nature make it an excellent scripting language.

Why is Python not the fastest language? ›

It is optimized for the purpose it is built: easy syntax, readable code and a lot of freedom for the developer. These design choices, however, do make Python code slower than other languages like C and Java.

How to find memory leaks in Python? ›

You can detect memory leaks in Python by monitoring your Python app's performance via an Application Performance Monitoring tool such as Scout APM.

Can Python cause memory leaks? ›

Code Snippets:

As sometimes, Garbage collectors fail to check on unreferenced objects, leading to memory leaks in Python. Eventually, python programs run out of memory because it gets filled by memory leaks. It becomes a challenge to find memory leaks in python and then to cure them.

Why do I keep getting memory errors? ›

Causes of such memory errors may be due to certain cognitive factors, such as spreading activation, or to physiological factors, including brain damage, age or emotional factors. Furthermore, memory errors have been reported in individuals with schizophrenia and depression.

Do we need to free memory in Python? ›

Also, programmers need not worry about deleting allocated memory, as it is taken care by Python memory manager. This leads to fewer memory leaks and better performance.

Does Python store data in memory? ›

Python Data Persistence - Introduction

Data so received, is stored in computer's main memory (RAM) in the form of various data structures such as, variables and objects until the application is running.

Does Python use static or dynamic memory? ›

As we know, Python uses the dynamic memory allocation which is managed by the Heap data structure.

Is Python memory safe? ›

Memory safe languages include Rust, Go, C#, Java, Swift, Python, and JavaScript. Languages that are not memory safe include C, C++, and assembly.

What is the difference between stack and heap memory in Python? ›

Key Difference Between Stack and Heap Memory

Stack accesses local variables only while Heap allows you to access variables globally. Stack variables can't be resized whereas Heap variables can be resized. Stack memory is allocated in a contiguous block whereas Heap memory is allocated in any random order.

How can I improve my memory coding? ›

Practice — Repeating increases the chances of a piece of information to be transferred from short-term memory to long-term memory. Neuroscientists have found that the ability to retrieve information improves proportionally to the number of days of practice [3].

Do you need a lot of memory for coding? ›

A laptop with 4GB of RAM should suffice. However, application or software developers who need to run virtual machines, emulators and IDEs to compile massive projects will need more RAM. A laptop with at least 8GB of RAM is ideal.

How do I limit CPU usage in Python? ›

Limiting CPU and Memory Usage

getrlimit() method. It returns the current soft and hard limit of the resource. Each resource is controlled by a pair of limits: a soft limit and a hard limit. The soft limit is the current limit, and may be lowered or raised by a process over time.

How does Python calculate memory variable size? ›

In python, the usage of sys. getsizeof() can be done to find the storage size of a particular object that occupies some space in the memory. This function returns the size of the object in bytes.

How to get CPU memory usage in Python? ›

Get current CPU usage using psutil

The function psutil. cpu_percent() provides the current system-wide CPU utilization in the form of a percentage. It takes a parameter which is the time interval (seconds). Since CPU utilization is calculated over a period of time it is recommended to provide a time interval.

How does Python measure performance? ›

Three popular ways to measure the performance of Python code are:
  1. Using the time. perf_counter() function.
  2. Using the timeit module.
  3. Using the profile and cProfile modules.
Oct 31, 2021

Does Python dynamically allocate memory? ›

As we know, Python uses the dynamic memory allocation which is managed by the Heap data structure. Memory Heap holds the objects and other data structures that will be used in the program. Python memory manager manages the allocation or de-allocation of the heap memory space through the API functions.

Is Python CPU intensive? ›

Running a regular Python program will not use all CPU cores by default. Instead, it will run on one CPU core until completion. This is a problem as modern systems have a CPU with 2, 4, 8, or more cores. This means your Python programs are only using a fraction of the capabilities of your system.

How to get GPU usage Python? ›

Installation
  1. Open a terminal in a folder other than the GPUtil folder.
  2. Start a python console by typing python in the terminal.
  3. In the newly opened python console, type: import GPUtil GPUtil. ...
  4. Your output should look something like following, depending on your number of GPUs and their current usage:

How do I check memory usage in Jupyter? ›

The jupyter-resource-usage extension is part of the default installation, and tells you how much memory your user is using right now, and what the memory limit for your user is. It is shown in the top right corner of the notebook interface.

Why is Python performance slow? ›

Unlike other popular programming languages including C# or JAVA, Python is dynamically typed and an interpreted language. It is slow primarily due to its dynamic nature and versatility.

Does Python scale well? ›

In most cases, scalability is viewed as how much load system can bear at a time (machine scalability). In that sense, Python is less scalable in terms of performance and execution speed than other programming languages like Java and C++ because of several reasons discussed below.

Is Python good for performance? ›

In this article we'll discover that Python is not a bad language that is just very slow. It is optimized for the purpose it is built: easy syntax, readable code and a lot of freedom for the developer. These design choices, however, do make Python code slower than other languages like C and Java.

References

Top Articles
Latest Posts
Article information

Author: Reed Wilderman

Last Updated: 15/12/2023

Views: 6414

Rating: 4.1 / 5 (72 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Reed Wilderman

Birthday: 1992-06-14

Address: 998 Estell Village, Lake Oscarberg, SD 48713-6877

Phone: +21813267449721

Job: Technology Engineer

Hobby: Swimming, Do it yourself, Beekeeping, Lapidary, Cosplaying, Hiking, Graffiti

Introduction: My name is Reed Wilderman, I am a faithful, bright, lucky, adventurous, lively, rich, vast person who loves writing and wants to share my knowledge and understanding with you.