Explain in simple terms: Detailed explanation of Python memory management mechanism

1. What is memory?

1.1. Introduction to RAM

随机存取存储器(Random Access Memory,RAM): is a hardware component in a computer used to temporarily store data. It is one of the main memories of the computer and is used to store running programs and data required by the operating system.

main feature:

  • temporary storage: The data stored in RAM is temporary, meaning it will be cleared when the computer is shut down or restarted. This is different from permanent storage devices such as hard drives, which hold data for long periods of time.
  • random access: RAM has random access capabilities, which means it can quickly access any data in storage without having to read it in a specific order. This makes RAM ideal for use as a computer's working memory to quickly access and process data.
  • high speed storage: RAM is a high-speed storage device where data can be read or written in milliseconds or even nanoseconds. This allows the computer to perform tasks quickly, improving performance.
  • Small capacity: The capacity of RAM is usually relatively small, usually measured in megabytes (MB) or gigabytes (GB). Your computer's RAM capacity affects its ability to multitask and run large programs.

1.2. RAM capacity

Capacity of RAM: Indicates the upper limit of the total memory of data and programs that the computer (currently/single time) can load and calculate at the same time. For example: If your current computer has 64GB of RAM, the total amount of data and programs that can be loaded and run at the same time cannot exceed 64GB. Failure to do so will result in insufficient (RAM) memory and may cause the system to become very slow or even system crash.

Memory usage:

  • Operating system: The operating system requires a portion of RAM to run the core functions of the system.
  • Running applications: Each open application needs to be allocated a portion of RAM to store its data and code. Larger applications and multitasking may require more RAM.
  • Data being processed: such as open documents, images, video or audio files, all require RAM to store.
  • Caching and temporary data: Operating systems and applications often use RAM to speed up the reading and writing of data, so some RAM is also used for caching and temporary storage.
    When data and programs that exceed the RAM capacity are loaded, computer performance will be affected because it will have to frequently read data from permanent storage devices such as hard drives or SSDs, which usually Slower, causing performance degradation.

1.3. Check computer memory

Insert image description here

1.4. Monitor computer memory

Current memory usage
Insert image description here

Current memory usage ratio
Insert image description here

2.RAM is the main memory of the CPU, and video memory is the dedicated memory of the GPU

RAM(随机存取存储器)是中央处理器CPU的主内存: Used to temporarily store data used by running programs and the operating system. It is a type of volatile memory, meaning that the data stored in it is lost when the computer is turned off.

  • Function: The CPU reads data and instructions through RAM to support various tasks when the computer is running.
  • Performance: RAM speed and capacity are critical to CPU performance. Larger and faster RAM delivers the required data more efficiently, reducing the time the CPU spends waiting for data.

显存(Graphics RAM)是图形处理器GPU的专用内存: Used to store graphics data, such as textures, frame buffers, depth buffers, etc.

  • Function: Video memory is high-speed memory dedicated to the GPU, optimized to provide fast access and high bandwidth.
  • Performance: The size and type of video memory (such as GDDR5, GDDR6, etc.) directly affect the GPU's ability to process large-scale graphics data. Larger capacity and higher bandwidth of video memory usually means that the GPU can handle more complex graphics tasks.
  • CPU (Central Processing Unit): The core of a computer system, responsible for performing system tasks, including controlling the operation of the computer, performing arithmetic and logical operations, managing system resources, etc. CPU has fewer cores (several to dozens) and is suitable for general computing.
  • GPU (Graphics Processing Unit): Originally designed for graphics rendering, it is also widely used in scientific computing, deep learning and other fields due to its parallel processing capabilities. GPU has a large number of small processing cores and is suitable for large-scale parallel computing.

Collaborative work: In deep learning, the CPU is responsible for managing tasks and scheduling work, while the GPU is used to accelerate large-scale matrix operations and increase training speed.

3. Memory management

3.0,Memory range of different data types

In computer memory, the calculation formula of image memory is:内存大小 = 宽度 × 高度 × 通道数 x 每个像素的字节数. The basic storage unit of memory is bytes rather than bits, and is represented by integers.

# 在计算机中,最小的存储单元是位(bit),而一个字节(Byte)通常由8个位组成。

1 TB = 1024 GB = [1024^2] MB = [1024^3] KB = [1024^4] Bytes = [1024^4 x 8] bits
位(bits) + 字节(Bytes) + 千字节(Kilobytes,KB) + 兆字节(Megabytes,MB) + 吉字节(Gigabytes,GB) + 千兆字节(Terabytes,TB) + PB + EB + ZB + YB
type of data illustrate Bytes per pixel data range memory range
bool Boolean type 1 person [0,1] 2bits
you8 Signed 8-bit integer 8 bits (1 byte) [-128,127] [-16,16]Bytes
uint8 unsigned 8-bit integer 8 bits (1 byte) [0,255] [0,32]Bytes
int16 Signed 16-bit integer 16 bits (2 bytes) [-32768,32767] [-32,32]KB
uint16 Unsigned 16-bit integer 16 bits (2 bytes) [0,65535] [0,64]KB
int32 Signed 32-bit integer 32 bits (4 bytes) [-2,147,483,648,2,147,483,647] [-2,2]GB
uint32 Unsigned 32-bit integer 32 bits (4 bytes) [0,4,294,967,295] [0,4]GB
int64 Signed 64-bit integer 64 bits (8 bytes) [-9.22×1018,9.22×1018] [-8,8]EB
uint64 Unsigned 64-bit integer 64 bits (8 bytes) [0,18,446,744,073,709,551,615] [0,16]EB

3.1,How does python allocate memory?

How the Python memory manager allocates memory:Select a large enough memory block based on the size of the object, and divide this memory into two parts, one for storing the object's data, and the other for storing the object's reference.
(1) In Python, memory is automatically allocated when an object is used, and memory is automatically released when the object is no longer used.
(2) In Python, objects are all dynamic types (that is, you do not need to specify the data type when declaring variables, python will automatically determine it), and they are all dynamic memory allocations.

3.2,python adopts automatic memory management mechanism

Python automatically manages memory (i.e. garbage collector) through "reference counting" and "cyclic reference detection". Therefore, under normal circumstances, programmers do not need to pay too much attention to memory release. Manual memory management only needs to be considered in special cases when dealing with large data sets or when memory needs to be reclaimed in a timely manner.

Automatic memory management mechanism: also called garbage collector (gc). Responsible for regularly scanning and automatically recycling unused memory and objects, so that developers can focus on program logic without having to worry about memory management issues.
(1) Garbage collector: The specific release timing is controlled by the internal strategy of the interpreter, rather than explicitly controlled by the programmer.
(2) Garbage collector: It does not run strictly according to a predetermined cycle, but collects according to a generational algorithm.

Garbage collector triggers:

  • (1) Manual garbage collection:Use gc.collect() to manually force garbage collection to solve the problem of accelerating the release of no longer needed memory when memory is sensitive (for example: insufficient memory).
    • 1. Python uses an automatic garbage collection mechanism to automatically detect objects that are no longer referenced and release their memory regularly in the background. Compared with gc.collect(), automatic garbage collection will have a waiting period (regular detection).
    • 2. Under normal circumstances, there is no need to manually use gc.collect(), because Python's garbage collection mechanism is usually smart enough and will automatically run at the appropriate time to release memory that is no longer needed.
    • 3, gc.collect() speeds up the execution of automatic garbage collection, rather than releasing memory immediately (but it is almost the same).
    • 4, gc.collect() itself will also cause additional overhead, so it is not recommended to trigger manual garbage collection frequently.
  • (2) Reference count:The garbage collection mechanism records the number of times each object is referenced by other objects.
    • 1. The reference count starts from 0;
    • 2. When the object has a new reference pointing to it, the number of references +1; when the reference pointing to the object expires (such as: del 对象), the number of references -1 .
    • 3, When the reference count of the object is 0 (such as: 对象=None), it is placed in the garbage collection queue, waiting for automatic garbage collection. rather than releasing the memory immediately.
      • [del object]: Use the del statement to delete the object from the namespace, and the memory will be released immediately. However, Python's memory pool mechanism will not immediately release the memory to the computer, but will wait for reuse.
      • [Object=None]: Set the object to None, the reference count of the object becomes zero, but it will not be released immediately, but will wait for the automatic garbage collection mechanism to release the memory.
  • (3) Circular reference detection:If there are mutual references between objects, a ring structure will be formed between the objects, so that the reference count will not drop to zero, so the memory cannot be automatically recycled, resulting in Memory leak. Generational recycling
    • 1, 引用链: used to track reference relationships between objects. When the reference count is not zero, but a circular reference is formed between objects.
    • 2, 分代回收(generation): divides all objects into three generations: 0, 1, and 2. All newly created objects are generation 0 objects; when objects of a certain generation survive garbage collection, they are upgraded to the next generation.
      • 11. When garbage collection starts, all generation 0 objects will be scanned.
      • 22. If generation 0 has gone through a certain number of garbage collections, scan and clean up generations 0 and 1.
      • 33. When generation 1 has also experienced a certain number of garbage collections, it will start scanning 0, 1, 2, that is, all objects.
  • (4) Memory pool: is used to improve the memory allocation and release efficiency of small memory objects. Frequent allocation and release of small memory objects may lead to memory fragmentation and performance degradation.
    • 1, Pre-allocate a fixed-size memory block: Python will select an appropriate memory block based on the size of the object. Each memory block contains multiple smaller blocks of the same size. Usually 8 bytes per block.
    • 2, Memory block status: Memory blocks can have different states, including free, allocated, and released. Python maintains the state of memory blocks.
    • 2, Object reuse: If the memory block contains released small blocks, Python will first reuse these small blocks to reduce Memory allocation overhead. This means that blocks of memory of the same size can be allocated and freed multiple times without having to interact with the operating system each time.
    • 3, Delayed release: Memory blocks that are no longer used will not be released immediately back to the operating system, but will be retained in the memory. Pool for future reuse (object reuse).
      优点: helps reduce the performance overhead caused by frequent memory allocation and release.
      缺点: may cause a memory leak because memory blocks that are no longer used are not immediately reclaimed by the operating system. Therefore, developers should avoid retaining long-term references to objects that are no longer used to avoid memory leaks.

Memory pool mechanism (category in pyramid model): Graphic understanding of memory pool
Insert image description here

  • 第-1层,第-2层: Controlled by the OS-specific virtual memory manager (VMM)
    (layer -1): Kernel dynamic storage allocation and management< /span>
    (Tier-2): Physical Memory (ROM/RAM) + Secondary Storage

    • ROM(只读存储器,Read-Only Memory): A storage device used to store firmware and fixed data for a computer or other electronic device. Commonly used to store the computer's boot program (BIOS). Unlike RAM, data in ROM generally cannot be modified.
    • RAM(随机访问存储器,Random Access Memory): A temporary memory storage device for storing running programs and data. Commonly used in computers to quickly read and write data. RAM is volatile and data is lost when power is removed.
    • 二级存储(交换,Secondary Storage): Refers to a non-volatile mass storage device, such ashard disk drive (HDD) or solid-state drive (SSD). It is used for long-term storage of data, files, and operating systems. Unlike RAM, data in secondary storage is not lost when power is lost.
  • 第0层: Memory allocation and memory release are performed by malloc and free of the underlying general-purpose allocator in the C standard library;

  • 第1层:When the requested memory is >256KB, the memory allocation is performed by Python's native memory allocator (raw memory allocator). Essentially, it calls functions such as malloc and realloc in the C standard library.

  • 第2层:When the requested memory is <256KB, memory allocation is performed by the Python object allocator.

  • 第3层: The user uses the direct operation layer of the object. Features: For python built-in objects (such as: int, dict, list, string, etc.), each data type All have independent private memory pools, and the memory pools between objects are not shared. For example: the memory released by int will not be allocated to float.

3.3. Disadvantages of python’s automatic memory management mechanism

  • (1)memory leak:After the program allocates memory, it cannot normally release the memory that is no longer used. This can ultimately cause the program to slow down or crash. Some common situations are as follows:
    • If a program allocates memory but does not free it when it is no longer needed, memory will leak.
    • If the data structure is not designed correctly, it is possible to retain references to objects when they are no longer needed, causing memory leaks.
    • Memory leaks can occur if there are circular references or incorrect reference counting.
    • If you open files and don't close them properly after use, memory will leak.
  • (2)performance degradation: Memory needs to be allocated and released at the same time, and the program may slow down.
  • (3)Not enough storage: When the memory required by the program is greater than the memory of the system space, the problem of insufficient memory will occur.

3.4. Python memory optimization methods

[How to optimize memory in python 1] + [How to optimize memory in python 2]

  • Reduce the usage of global variables: Global variables will exist until the end of the program, so they will always occupy memory. Use local variables as much as possible if not necessary, and release them as soon as they are no longer needed.
  • Avoid creating unnecessary objects: In Python, creating objects is a way of allocating memory. Therefore, try to avoid creating unnecessary objects and reduce the number of memory allocations by reusing objects.
  • Manually release unnecessary objects:Use gc.collect() to manually force garbage collection, which is used to immediately release no longer needed memory when memory is sensitive (for example: insufficient memory).

4. Project actual combat

4.1. View the reference count of an object

import sys

def create_objects():
    obj1 = [1, 2, 3]			    # 创建对象      (obj1对象的引用次数=2)
    obj2 = [obj1, 1]		        # 创建对象      (obj2对象的引用次数=1)
    obj3 = {
    
    'a': 1, 'b': 2}		    # 创建对象      (obj3对象的引用次数=1)
    print(sys.getrefcount(obj1))  # 获取对象a的引用次数
    print(sys.getrefcount(obj2))  # 获取对象a的引用次数
    print(sys.getrefcount(obj3))  # 获取对象a的引用次数
    #########################################################################
    obj1 = None  		# 将不再使用对象的引用设置为None     (obj2对象的引用次数=0)
    del obj2  		    # 将不再使用对象的引用设置为None     (obj1对象的引用次数=0)
    print(sys.getrefcount(obj1))  # 获取对象a的引用次数
    print(sys.getrefcount(obj3))  # 获取对象a的引用次数
    return

create_objects()  # 创建对象

"""###################################################################
# 函数:sys.getrefcount(a): 返回对象a的引用计数。
# 注意: 函数内部会增加一次临时引用计数来获取对象的引用数,但该函数执行之后会自动减去临时引用计数,以保持对象的引用计数不变。
# 
# 【del 对象】:	(1)使用del语句将对象从命名空间中删除,该对象的内存将被立即释放;
# 				(2)但Python的内存池机制导致该部分占用的内存不会立即释放给计算机,而是等待复用。
# 【对象=None】:	(1)将对象设置为None,该对象的引用计数变为零;
# 				(2)但内存不会立即释放,而是等待自动垃圾回收机制进行内存释放。
###################################################################"""

4.2. Memory pool: Set the i-th generation threshold for garbage collection

import gc
gc.set_threshold(700, 10, 5)

"""###################################################################
# 函数功能:设置垃圾回收的第i代阈值。
# 函数简介:gc.set_threshold(threshold0, threshold1, threshold2)
# 输入参数:    
#            threshold0      是垃圾回收的第0代阈值。当0代的垃圾数量达到这个值时,触发0代的垃圾回收。
#            threshold1      是垃圾回收的第1代阈值。当0代的垃圾数量达到 threshold0,且0和1两代垃圾的总数达到 threshold1,则触发两代的垃圾回收。
#            threshold2      是垃圾回收的第2代阈值。当0和1两代垃圾的总数达到 threshold1,且同时0/1/2三代垃圾的总数达到 threshold2,则触发三代的垃圾回收。
###################################################################"""

4.3,Get system memory + get process (actual memory + peak memory)

def memory_usage():
    import psutil

    # (1)获取系统内存信息
    mem_info = psutil.virtual_memory()
    total_memory = mem_info.total / (1024 ** 3)  # 总内存大小(字节 - GB)
    used_memory = mem_info.used / (1024 ** 3)  # 已使用内存(字节 - GB)
    free_memory = mem_info.available / (1024 ** 3)  # 空闲的内存(字节 - GB)
    print(f"系统总内存RAM: {
      
      total_memory} GB")
    print(f"系统已占用内存: {
      
      used_memory} GB")
    print(f"系统未占用内存: {
      
      free_memory} GB")
    print("*" * 50)

    # (2)获取进程的内存信息
    process = psutil.Process()  # 创建一个进程对象
    mem_info = process.memory_info()  # 获取当前进程在RAM中的内存使用量
    memory_usage = mem_info.rss / (1024 ** 3)  # 表示进程在当前时刻的实际内存使用情况(字节 - GB)
    peak_memory = mem_info.peak_wset / (1024 ** 3)  # 表示进程在任意时间点的内存使用的峰值(字节 - GB)
    print(f"当前进程实际占用内存: {
      
      memory_usage_mb:.8f} GB")
    print(f"当前进程最大占用内存: {
      
      peak_memory_mb:.8f} GB")

    return memory_usage, peak_memory


if __name__ == "__main__":
    memory_usage()  # 创建对象

"""
系统总内存RAM: 63.74748229980469 GB
系统已占用内存: 8.997417449951172 GB
系统未占用内存: 54.750064849853516 GB
**************************************************
当前进程实际占用内存: 0.01511765 GB
当前进程最大占用内存: 0.01512146 GB
"""

4.4,Manually release memory

"""########################################################################
# 函数: gc.collect(): 手动垃圾回收管理
# 功能:
#     若使用gc.collect():	(1)不能保证立即释放内存,但可以加速自动垃圾回收的执行速度。
#                           (2)其本身也会引起额外的开销,不建议频繁触发手动垃圾回收。
#     若不使用gc.collect():	垃圾回收器是自动定期检测并回收内存,但有一定延迟(定期)。
########################################################################
# 【del 对象】:	(1)使用del语句将对象从命名空间中删除,该对象的内存将被立即释放;
# 				(2)但Python的内存池机制导致该部分占用的内存不会立即释放给计算机,而是等待复用。
# 【对象=None】:	(1)将对象设置为None,该对象的引用计数变为零;
# 				(2)但内存不会立即释放,而是等待自动垃圾回收机制进行内存释放。
########################################################################"""
import gc
import numpy as np


def memory_usage():
    """用于获取当前程序的内存占用情况(单位:MB)"""
    import psutil
    process = psutil.Process()  # 创建一个进程对象,用于获取当前程序的内存信息。
    mem_info = process.memory_info()  # 获取当前进程在RAM中的内存使用量
    memory_usage = mem_info.rss / (1024 ** 2)  # 表示进程在当前时刻的实际内存使用情况(字节 - MB)
    peak_memory = mem_info.peak_wset / (1024 ** 2)  # 表示进程在任意时间点的内存使用的峰值(字节 - MB)
    return memory_usage, peak_memory
    # 1 TB = 1024 GB = [1024^2] MB = [1024^3] KB = [1024^4] Bytes = [1024^4 x 8] bits


if __name__ == "__main__":
    # (1)查看系统初始的内存使用情况
    system_memory, peak_memory = memory_usage()
    print(f"系统初始的内存使用情况(MB): {
      
      system_memory:.2f}")
    ######################################################
    # (2)创建一个数组
    array = np.random.randint(0, 100, size=(400, 500, 600))
    print(f"总内存使用情况(MB): {
      
      memory_usage()[0]:.2f}, {
      
      memory_usage()[1]:.2f}")
    ######################################################
    # (3)查看系统进程的内存使用情况
    array[array <= 2800] = 0  # 灰度强度滤波
    print(f"总内存使用情况(MB): {
      
      memory_usage()[0]:.2f}, {
      
      memory_usage()[1]:.2f}")
    ######################################################
    # (4)查看(手动垃圾回收)系统进程的内存使用情况
    array = None
    gc.collect()  # 手动垃圾回收:加速自动垃圾回收
    print(f"总内存使用情况(MB): {
      
      memory_usage()[0]:.2f}, {
      
      memory_usage()[1]:.2f}")

    """
    系统初始的内存使用情况(MB): 29.73
    总内存使用情况(MB): 487.60, 487.61
    总内存使用情况(MB): 487.61, 602.06
    总内存使用情况(MB): 29.85, 602.06
    """

Guess you like

Origin blog.csdn.net/shinuone/article/details/133982613