To properly free GPU memory in PyTorch, you must explicitly delete references to unused tensors and variables using the
del keyword. Afterward, invoke
torch.cuda.empty_cache() to release the cached memory back to the system. This two-step process ensures that your deep learning models do not encounter out-of-memory errors during training or inference.
Understanding PyTorch Memory Management
PyTorch uses a caching allocator to manage GPU memory efficiently. When you allocate tensors on the GPU, PyTorch does not immediately return the memory to the operating system when the Python variable goes out of scope. Instead, it stores this memory in a cache for potential future allocations. While this speeds up subsequent operations, it can lead to apparent memory leaks if developers are unaware of how the caching mechanism works. The actual GPU memory usage reported by tools like
nvidia-smi may remain high even after you have finished using specific models or data loaders. Understanding this distinction between allocated and reserved memory is crucial for effective resource management.
Step-by-Step Guide to Clearing Memory
The most common scenario where you need to free memory is when switching between different models, clearing intermediate activations, or preparing for a new training run. Follow these steps to ensure a clean slate.
First, identify all objects that are consuming GPU memory. These typically include model parameters, input tensors, target labels, and intermediate activation maps. You should remove any references to these objects from your current namespace. In Python, this is done using the
del statement. For example, if you have a model named
my_model, you would execute
del my_model. Similarly, delete any large batch tensors that are no longer needed.
Second, after deleting the Python references, call
torch.cuda.empty_cache(). This function tells the PyTorch CUDA backend to release all unused cached memory back to the system. Without this step, the memory remains reserved by PyTorch and unavailable for other processes or new allocations, even though the Python variables pointing to it have been deleted.
Here is a concrete example of this workflow:
# Assume 'model' and 'data' are large GPU objects
del model
del data
del optimizer
# Release the cached memory
torch.cuda.empty_cache()
# Verify the freed memory
print(torch.cuda.memory_allocated())
It is important to note that
empty_cache() is idempotent. Calling it multiple times has no negative side effects, but it does incur a small performance overhead. Therefore, it is best practice to call it only when you genuinely need to reclaim memory for a new operation, rather than in every loop iteration unless necessary.
Common Pitfalls and Best Practices
Developers often make the mistake of assuming that garbage collection will automatically handle GPU memory. Python's garbage collector manages CPU memory and object lifecycles, but it does not interact with the GPU driver directly. If you rely solely on garbage collection, you may find that your GPU memory usage grows monotonically until the system crashes. Always be explicit about what you want to discard.
Another pitfall involves keeping references to tensors unintentionally. If you store a tensor in a list or dictionary and never remove it, it will persist in memory. Use context managers or careful scoping to limit the lifetime of temporary tensors. Additionally, when working with large datasets, use generators or iterators to load data in batches rather than loading entire datasets into RAM or VRAM at once.
For more information on optimizing machine learning workflows, you might find our guide on
Improving Machine Learning Algorithms: A Comprehensive Guide helpful. It covers broader strategies for efficiency beyond just memory management.
Monitoring GPU Usage
Before freeing memory, it is useful to check the current state. You can use
torch.cuda.memory_allocated() to see how much memory is currently occupied by tensors. To see the total reserved memory including the cache, use
torch.cuda.memory_reserved(). Comparing these values helps diagnose whether you are dealing with active tensor usage or cached memory that needs flushing.
You can also monitor memory usage via the command line using
nvidia-smi. This tool shows the total GPU memory, used memory, and free memory. If
nvidia-smi shows high usage but
torch.cuda.memory_allocated() shows low usage, it indicates that PyTorch is holding onto cached memory. In this case, calling
empty_cache() will resolve the discrepancy.
For those interested in related deep learning topics, such as handling complex architectures, you may want to read
Training Video Classifier: An Overview, which discusses memory-intensive tasks common in video processing.
Frequently Asked Questions
Does del() immediately free GPU memory?
No,
del() only removes the reference to the tensor in Python. The actual memory is not returned to the OS until
torch.cuda.empty_cache() is called. You must combine both steps to effectively reduce the memory footprint visible to other applications.
Is torch.cuda.empty_cache() slow?
It is relatively fast but not instantaneous. It requires synchronization with the GPU device, which introduces a small latency. Avoid calling it inside tight training loops unless you are running out of memory; instead, call it between epochs or when switching models.
Why does nvidia-smi still show high memory usage after empty_cache?
Sometimes the GPU driver or other processes may hold onto memory. Additionally, if you have not deleted all relevant Python variables, the tensors are still allocated. Ensure you have deleted all models, optimizers, and data tensors before calling the cache clear function.
Can I free memory without restarting the kernel?
Yes, by explicitly deleting variables and calling
torch.cuda.empty_cache(). However, if memory fragmentation becomes severe, restarting the Jupyter kernel or Python script is the most reliable way to guarantee a completely clean memory state.
What happens if I don't free GPU memory?
You will eventually hit an Out Of Memory (OOM) error. This stops your training or inference process abruptly. Proper memory management allows you to train larger models or use larger batch sizes by reusing the same physical VRAM efficiently throughout the session.