Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Quản lý bộ nhớ là quá trình quản lý hiệu quả bộ nhớ máy tính (RAM). Nó liên quan đến việc phân bổ một phần bộ nhớ vào thời gian chạy cho chương trình khi chương trình yêu cầu và giải phóng bộ nhớ được phân bổ để sử dụng lại khi chương trình không còn cần nó nữa.

Trong các ngôn ngữ như C hoặc Rust, quản lý bộ nhớ là trách nhiệm của lập trình viên. Lập trình viên phải phân bổ thủ công bộ nhớ trước khi chương trình có thể sử dụng và phát hành nó khi chương trình không còn cần nó nữa. Trong Python, quản lý bộ nhớ là tự động! Python tự động xử lý việc phân bổ và giải quyết bộ nhớ.

Trong bài viết này, chúng tôi sẽ thảo luận về các phần bên trong của quản lý bộ nhớ trong Python. Chúng tôi cũng sẽ đề cập đến cách các đơn vị cơ bản, chẳng hạn như các đối tượng, được lưu trữ trong bộ nhớ, các loại phân bổ bộ nhớ khác nhau trong Python và cách trình quản lý bộ nhớ của Python quản lý bộ nhớ một cách hiệu quả.

Hiểu nội bộ của quản lý bộ nhớ trong Python giúp thiết kế các ứng dụng hiệu quả bộ nhớ. Nó cũng giúp dễ dàng gỡ lỗi các vấn đề bộ nhớ trong một ứng dụng.

Mục lục

  • Python như một đặc điểm kỹ thuật ngôn ngữ
  • Cpython là gì
    • Các hàm
      i = 0
      
      while i < 100:
          i = i + 1
      
      
      8 và
      i = 0
      
      while i < 100:
          i = i + 1
      
      
      9 trong C là gì?
    • Đối tượng trong Python
    • Các biến trong Python
  • Quản lý bộ nhớ trong CPython
    • Bộ phân bổ bộ nhớ
    • Người phân bổ đối tượng
    • Khối
    • Hồ bơi
    • Đấu trường
  • Liệu một quá trình Python có phát hành bộ nhớ không?
  • Bộ sưu tập rác trong Python
    • Số lượng tham chiếu
    • Bộ sưu tập rác trên cơ sở số lượng tham chiếu
    • Tài liệu tham khảo theo chu kỳ trong Python
    • Bộ sưu tập rác thế hệ
    • i = 0  # malloc(i)
      
      while i < 100:
          # malloc(i + 1)
          # free(i)
          i = i + 1
      
      0 Mô -đun trong Python

Hãy bắt đầu với việc hiểu Python như một đặc điểm kỹ thuật ngôn ngữ và sau đó đi sâu vào Cpython!

Python như một đặc điểm kỹ thuật ngôn ngữ

Cpython là gì

Các hàm

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9 trong C là gì?

Đối tượng trong Python

Các biến trong Python

Quản lý bộ nhớ trong CPython

Bộ phân bổ bộ nhớ

Người phân bổ đối tượng

Khối

Hồ bơi

Đấu trường

Liệu một quá trình Python có phát hành bộ nhớ không?

Bộ sưu tập rác trong Python

Số lượng tham chiếu

Bộ sưu tập rác trên cơ sở số lượng tham chiếu

Tài liệu tham khảo theo chu kỳ trong Python

Bộ sưu tập rác thế hệ

Đối tượng trong Python

Các biến trong Python

Mỗi đối tượng Python bao gồm ba trường:

  • Giá trị
  • Loại hình
  • Số lượng tham chiếu

Hãy xem xét một ví dụ đơn giản:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Khi mã trên được thực thi, CPython sẽ tạo một đối tượng loại

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
3 và phân bổ bộ nhớ cho đối tượng này trên bộ nhớ heap.

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
4 chỉ ra loại đối tượng trong CPython và trường
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
5, như tên cho thấy, lưu trữ giá trị của đối tượng (
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
6 trong trường hợp này). Chúng tôi sẽ thảo luận về lĩnh vực
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
7 sau trong bài viết.

Các biến trong Python

Các biến trong Python chỉ là các tham chiếu đến đối tượng thực tế trong bộ nhớ. Chúng giống như tên hoặc nhãn trỏ đến đối tượng thực tế trong bộ nhớ. Họ không lưu trữ bất kỳ giá trị nào.

Xem xét ví dụ sau:

Như đã thảo luận trước đó, khi mã trên được thực thi, CPython bên trong tạo ra một đối tượng của số nguyên loại. Biến

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 trỏ đến đối tượng số nguyên này như được hiển thị bên dưới:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Chúng ta có thể truy cập đối tượng số nguyên trong chương trình Python bằng cách sử dụng biến

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8.

Hãy gán đối tượng số nguyên này cho một biến khác

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0:

Khi mã trên được thực thi, các biến

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 và
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 đều trỏ đến cùng một đối tượng số nguyên, như được hiển thị bên dưới:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Bây giờ chúng ta hãy tăng giá trị của đối tượng số nguyên bằng 1:

# Increment a by 1
a = a + 1

Khi mã trên được thực thi, CPython sẽ tạo một đối tượng số nguyên mới với giá trị

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
3 và tạo biến
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 trỏ đến đối tượng số nguyên mới này. Biến
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 sẽ tiếp tục trỏ đến đối tượng số nguyên với giá trị
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
6, như được hiển thị bên dưới:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Ở đây, chúng ta có thể thấy rằng thay vì ghi đè giá trị của

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
6 với
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
3, CPython tạo ra một đối tượng mới với giá trị
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
3 vì các số nguyên trong Python là bất biến. Sau khi được tạo, chúng không thể được sửa đổi. Xin lưu ý rằng các loại dữ liệu nổi và chuỗi cũng không thể thay đổi trong Python.

Hãy xem xét một chương trình Python đơn giản để giải thích thêm về khái niệm này:

i = 0

while i < 100:
    i = i + 1

Mã trên xác định một vòng lặp

import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
0 đơn giản tăng giá trị của biến
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1 cho đến khi nó nhỏ hơn
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
6. Khi mã này được thực thi, với mỗi lần tăng của biến
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1, cpython sẽ tạo một đối tượng số nguyên mới với giá trị được tăng lên và đối tượng số nguyên cũ sẽ bị xóa (chính xác hơn, đối tượng này sẽ đủ điều kiện để xóa) khỏi bộ nhớ .

CPYThon gọi phương thức

i = 0

while i < 100:
    i = i + 1

8 cho mỗi đối tượng mới để phân bổ bộ nhớ cho đối tượng đó. Nó gọi phương thức
i = 0

while i < 100:
    i = i + 1

9 để xóa đối tượng cũ khỏi bộ nhớ.

Hãy chuyển đổi mã trên theo các điều khoản của

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9:

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1

Chúng ta có thể thấy rằng Cpython tạo và xóa một số lượng lớn các đối tượng, ngay cả đối với chương trình đơn giản này. Nếu chúng tôi gọi các phương thức

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9 cho mỗi lần tạo và xóa đối tượng, nó sẽ làm giảm hiệu suất thực hiện của chương trình và làm cho chương trình chậm.

Do đó, CPython giới thiệu các kỹ thuật khác nhau để giảm số lần chúng ta phải gọi

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9 cho mỗi lần tạo và xóa đối tượng nhỏ. Bây giờ hãy hiểu cách CPython quản lý bộ nhớ!

Quản lý bộ nhớ trong CPython

Quản lý bộ nhớ trong Python liên quan đến việc quản lý một đống riêng tư. Một đống riêng tư là một phần của bộ nhớ dành riêng cho quá trình Python. Tất cả các đối tượng Python và cấu trúc dữ liệu được lưu trữ trong đống riêng tư.

Hệ điều hành không thể phân bổ phần bộ nhớ này cho một quá trình khác. Kích thước của đống riêng có thể phát triển và thu nhỏ dựa trên các yêu cầu bộ nhớ của quá trình Python. Các đống riêng tư được quản lý bởi Trình quản lý bộ nhớ Python được xác định bên trong mã CPython.

Đối với mục đích đại diện, đống riêng tư trong CPython có thể được chia thành nhiều phần như hình dưới đây:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Xin lưu ý rằng ranh giới của mỗi phần này không cố định và có thể phát triển hoặc thu nhỏ dựa trên yêu cầu.

  1. Bộ nhớ phi khách hàng lõi Python: Một phần của bộ nhớ được phân bổ cho dữ liệu phi vật thể lõi Python.: Portion of the memory allocated to python core non-object data.
  2. Bộ đệm bên trong: Một phần của bộ nhớ được phân bổ cho bộ đệm bên trong.: Portion of the memory allocated to internal buffers.
  3. Bộ nhớ dành riêng cho đối tượng-Phần của bộ nhớ được phân bổ cho các đối tượng có bộ phân bổ bộ nhớ dành riêng cho đối tượng. - Portion of the memory allocated to objects that have object-specific memory allocators.
  4. Bộ nhớ đối tượng: Phần của bộ nhớ được phân bổ cho các đối tượng.: Portion of the memory allocated for objects.

Khi chương trình yêu cầu bộ nhớ, CPython sử dụng phương thức

i = 0

while i < 100:
    i = i + 1

8 để yêu cầu bộ nhớ đó từ hệ điều hành và đống riêng tư tăng kích thước.

Để tránh gọi

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9 cho mỗi lần tạo và xóa đối tượng nhỏ, CPython xác định nhiều người phân bổ và người bán hàng cho các mục đích khác nhau. Chúng tôi sẽ thảo luận chi tiết về từng người trong số họ trong phần tiếp theo!

Bộ phân bổ bộ nhớ

Để tránh gọi các phương thức

i = 0

while i < 100:
    i = i + 1

8 và
i = 0

while i < 100:
    i = i + 1

9 thường xuyên, CPython xác định một hệ thống phân cấp của các trình phân bổ, như được hiển thị bên dưới:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Đây là hệ thống phân cấp của các bộ phân bổ bộ nhớ từ cấp cơ sở:

  • Phương thức phân bổ mục đích chung (phương pháp
    i = 0
    
    while i < 100:
        i = i + 1
    
    
    8 của CPython)
  • Bộ phân bổ bộ nhớ thô (cho các đối tượng lớn hơn 512 byte)
  • Trình phân bổ đối tượng (cho các đối tượng nhỏ hơn hoặc bằng 512 byte)
  • Bộ phân bổ dành riêng cho đối tượng (Bộ phân bổ bộ nhớ cụ thể cho các loại dữ liệu cụ thể)

Ở cấp độ cơ sở là bộ phân bổ

def getrefcount(var):
    ...
8. Phân bổ
def getrefcount(var):
    ...
8 là phương pháp
i = 0

while i < 100:
    i = i + 1

8 của ngôn ngữ C cho CPython. Nó chịu trách nhiệm tương tác với Trình quản lý bộ nhớ ảo của hệ điều hành và phân bổ bộ nhớ cần thiết cho quy trình Python. Đây là người phân bổ duy nhất giao tiếp với Trình quản lý bộ nhớ ảo của hệ điều hành.

Ở đầu bộ phân bổ

def getrefcount(var):
    ...
8 là trình phân bổ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3
2 của Python. Bộ phân bổ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3
2 cung cấp một sự trừu tượng hóa cho bộ phân bổ
def getrefcount(var):
    ...
8 (tức là, phương pháp
i = 0

while i < 100:
    i = i + 1

8). Khi quá trình Python cần bộ nhớ, trình phân bổ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3
2 tương tác với trình phân bổ
def getrefcount(var):
    ...
8 để cung cấp bộ nhớ cần thiết. Nó đảm bảo rằng có đủ bộ nhớ để lưu trữ tất cả dữ liệu của quy trình Python.

Trên đầu trang

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3
2, chúng tôi có trình phân bổ đối tượng. Bộ phân bổ này được sử dụng để phân bổ bộ nhớ cho các đối tượng nhỏ (nhỏ hơn hoặc bằng 512 byte). Nếu một đối tượng cần hơn 512 byte bộ nhớ, Trình quản lý bộ nhớ của Python trực tiếp gọi Trình phân bổ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3
2.

Như đã thấy trong biểu diễn trên, chúng ta có các phân bổ dành riêng cho đối tượng trên đầu của bộ phân bổ đối tượng. Các loại dữ liệu đơn giản, chẳng hạn như số nguyên, float, chuỗi và danh sách, có các phân bổ dành riêng cho đối tượng tương ứng. Các phân bổ dành riêng cho đối tượng này thực hiện các chính sách quản lý bộ nhớ theo yêu cầu của đối tượng. Ví dụ, bộ phân bổ dành riêng cho đối tượng cho số nguyên có một triển khai khác với bộ phân bổ dành riêng cho đối tượng cho float.

Cả hai bộ phân bổ và phân bổ đối tượng dành riêng cho đối tượng đều hoạt động trên bộ nhớ đã được phân bổ cho quy trình Python bằng bộ phân bổ bộ nhớ thô. Những người phân bổ này không bao giờ yêu cầu bộ nhớ từ hệ điều hành. Họ hoạt động trên đống riêng tư. Nếu bộ phân bổ đối tượng hoặc bộ phân bổ dành riêng cho đối tượng cần nhiều bộ nhớ hơn, bộ phân bổ bộ nhớ thô của Python cung cấp nó bằng cách tương tác với bộ phân bổ đa năng.

Phân cấp phân cấp bộ nhớ trong Python

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Khi một đối tượng yêu cầu bộ nhớ và đối tượng có trình phân bổ dành riêng cho đối tượng được xác định, các phân bổ dành riêng cho đối tượng được sử dụng để phân bổ bộ nhớ.

Nếu đối tượng không có trình phân bổ dành riêng cho đối tượng và hơn 512 byte bộ nhớ được yêu cầu, trình quản lý bộ nhớ Python trực tiếp gọi bộ phân bổ bộ nhớ thô để phân bổ bộ nhớ.

Nếu kích thước bộ nhớ được yêu cầu nhỏ hơn 512 byte, các bộ phân bổ đối tượng được sử dụng để phân bổ nó.

Người phân bổ đối tượng

Bộ phân bổ đối tượng cũng được gọi là

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
0. Nó được sử dụng để phân bổ bộ nhớ cho các đối tượng nhỏ có kích thước nhỏ hơn 512 byte.

Cơ sở mã Cpython mô tả Trình phân bổ đối tượng là

Một bộ phân bổ bộ nhớ đa năng nhanh, đặc biệt cho các khối nhỏ, được sử dụng trên đỉnh của một malloc đa năng.

Nó được gọi cho mọi phân bổ và phân bổ đối tượng (pyobject_new/del) trừ khi các phân bổ dành riêng cho đối tượng thực hiện sơ đồ phân bổ độc quyền (ví dụ: ints sử dụng danh sách miễn phí đơn giản).

Đây cũng là nơi người thu gom rác tuần hoàn hoạt động có chọn lọc trên các đối tượng container.

Khi một đối tượng nhỏ yêu cầu bộ nhớ, thay vì chỉ phân bổ bộ nhớ cho đối tượng đó, trình phân bổ đối tượng yêu cầu một khối bộ nhớ lớn từ hệ điều hành. Khối bộ nhớ lớn này sau đó được sử dụng để phân bổ bộ nhớ cho các đối tượng nhỏ khác.

Bằng cách này, người phân bổ đối tượng tránh gọi

i = 0

while i < 100:
    i = i + 1

8 cho mỗi đối tượng nhỏ.

Khối bộ nhớ lớn mà bộ phân bổ đối tượng phân bổ được gọi là

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
2. Đấu trường có kích thước 256 kb.

Để sử dụng

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
3 một cách hiệu quả, cpython chia
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
2 thành
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
5. Hồ bơi là 4 kb. Vì vậy, một
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
6 có thể bao gồm 64 nhóm (256kb / 4kb).

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Các hồ bơi được chia thành

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
7.

Tiếp theo, chúng tôi sẽ thảo luận về từng thành phần này!

Khối

Các khối là đơn vị nhỏ nhất của bộ nhớ mà bộ phân bổ đối tượng có thể phân bổ cho một đối tượng. Một khối có thể được phân bổ cho chỉ một đối tượng và một đối tượng chỉ có thể được phân bổ cho một khối. Không thể đặt các phần của một đối tượng trong hai hoặc nhiều khối riêng biệt.

Các khối có sẵn trong các kích cỡ khác nhau. Kích thước nhỏ nhất của một khối là 8 byte, trong khi kích thước tối đa của một khối là 512 byte. Kích thước của một khối là bội số của 8, và do đó, kích thước khối có thể là 8, 16, 32, ..., 504 hoặc 512 byte. Mỗi kích thước khối được gọi là lớp kích thước. Có 64 lớp kích thước, như được hiển thị dưới đây:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Như đã thấy trong bảng trên, các khối lớp 0 có kích thước có kích thước 8 byte, trong khi kích thước khối lớp 1 có kích thước 16 byte, v.v.

Các chương trình luôn được phân bổ một khối đầy đủ hoặc không có khối nào cả. Vì vậy, nếu một chương trình yêu cầu 14 byte bộ nhớ, nó được phân bổ một khối 16 byte. Tương tự, nếu một chương trình yêu cầu 35 byte bộ nhớ, một khối 40 byte được phân bổ.

Hồ bơi

Một hồ bơi bao gồm các khối chỉ có một lớp kích thước. Ví dụ: một nhóm có khối kích thước lớp 0 không thể có các khối của bất kỳ lớp kích thước nào khác.

Kích thước của nhóm bằng kích thước của trang bộ nhớ ảo. Ở đây 'Trang bộ nhớ ảo thuật ngữ có nghĩa là gì:

Một trang, trang bộ nhớ hoặc trang ảo là một khối liên tục có độ dài cố định của bộ nhớ ảo. Đây là đơn vị nhỏ nhất của dữ liệu để quản lý bộ nhớ trong hệ điều hành bộ nhớ ảo. - Wikipedia

Trong hầu hết các trường hợp, kích thước của hồ bơi là 4 kb.

Các hồ bơi chỉ được chạm khắc từ các đấu trường khi không có hồ bơi nào khác có một khối của lớp kích thước được yêu cầu.

Một hồ bơi có thể ở một trong ba tiểu bang:

  1. Được sử dụng: Một nhóm được cho là ở trạng thái

    import sys
    
    ref_count = getrefcount(a)
    
    print(ref_count) # Output: 2
    
    8 nếu nó có sẵn các khối để phân bổ.: A pool is said to be in the
    import sys
    
    ref_count = getrefcount(a)
    
    print(ref_count) # Output: 2
    
    8 state if it has blocks available for allocation.

  2. Đầy đủ: Một hồ bơi được cho là ở trạng thái

    import sys
    
    ref_count = getrefcount(a)
    
    print(ref_count) # Output: 2
    
    9 nếu tất cả các khối của hồ bơi được phân bổ.: A pool is said to be in the
    import sys
    
    ref_count = getrefcount(a)
    
    print(ref_count) # Output: 2
    
    9 state if all the blocks of the pool are allocated.

  3. Trống Một hồ bơi được cho là ở trạng thái

    import sys
    
    ref_count = getrefcount(b)
    
    print(ref_count) # Output: 2
    
    0 nếu tất cả các khối của nhóm có sẵn để phân bổ. Một nhóm trống không có lớp kích thước liên quan đến nó. Nó có thể được sử dụng để gán các khối của bất kỳ lớp kích thước nào. A pool is said to be in the
    import sys
    
    ref_count = getrefcount(b)
    
    print(ref_count) # Output: 2
    
    0 state if all the blocks of the pool are available for allocation. An empty pool has no size class associated with it. It can be used to assign blocks of any size class.

Các nhóm được xác định trong mã Cpython như hình dưới đây:

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};

Thuật ngữ

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
1 chỉ ra lớp kích thước của nhóm. Nếu
import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
1 là 0 cho một nhóm, nó sẽ chỉ có các khối kích thước lớp 0 (nghĩa là, các khối 8 byte).

Thuật ngữ

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
3 chỉ ra đấu trường mà nhóm thuộc về.

Các nhóm có cùng lớp kích thước được liên kết với nhau bằng danh sách được liên kết gấp đôi. Con trỏ

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
4 trỏ đến nhóm tiếp theo của cùng một lớp kích thước, trong khi con trỏ
import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
5 trỏ đến nhóm trước đó có cùng lớp kích thước.

Đây là cách các nhóm có cùng lớp được kết nối:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Con trỏ

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
6 chỉ vào đầu danh sách các khối miễn phí được liên kết đơn lẻ trong nhóm. Khi một khối được phân bổ được giải phóng, nó được chèn ở phía trước của con trỏ
import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
6.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Như đã thấy trong hình trên, các khối

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
8 được phân bổ cho các đối tượng, trong khi các khối
i = 0

while i < 100:
    i = i + 1

9 được phân bổ cho các đối tượng nhưng bây giờ là miễn phí và có thể được phân bổ cho các đối tượng mới.

Khi một yêu cầu được thực hiện cho bộ nhớ và không có nhóm nào có khối lớp kích thước được yêu cầu, CPython khắc một nhóm mới từ đấu trường.

Khi một hồ bơi mới được chạm khắc, toàn bộ hồ bơi không được phân mảnh ngay lập tức thành các khối. Các khối được chạm khắc từ hồ bơi khi cần thiết. Vùng bóng mờ

i = 0

while i < 100:
    i = i + 1

00 của hồ bơi trong hình trên cho thấy các phần này của nhóm chưa được phân mảnh thành các khối.

Một đoạn trích từ cơ sở mã Cpython đề cập đến các khối khắc từ nhóm như sau:

Các khối có sẵn trong một nhóm không được liên kết tất cả với nhau khi một nhóm được khởi tạo. Thay vào đó, chỉ các khối "hai đầu tiên" (địa chỉ thấp nhất) được thiết lập, trả lại khối đầu tiên như vậy và đặt pool-> freeblock thành danh sách một khối giữ khối thứ hai như vậy. Điều này phù hợp với pymalloc phấn đấu ở tất cả các cấp (đấu trường, nhóm và khối) không bao giờ chạm vào một mảnh bộ nhớ cho đến khi nó thực sự cần thiết.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Ở đây, chúng ta có thể thấy rằng khi một hồ bơi mới được chạm khắc từ một đấu trường, chỉ có hai khối đầu tiên được chạm khắc từ hồ bơi. Một khối được phân bổ cho đối tượng yêu cầu bộ nhớ, trong khi khối còn lại miễn phí hoặc không bị ảnh hưởng và con trỏ

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
6 trỏ đến khối này.

CPython duy trì một mảng có tên

i = 0

while i < 100:
    i = i + 1

02 để theo dõi các nhóm ở trạng thái
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8 (các nhóm có sẵn để phân bổ) của tất cả các lớp kích thước.

Chỉ số của mảng

i = 0

while i < 100:
    i = i + 1

02 bằng với lớp kích thước của nhóm. Đối với mỗi chỉ mục
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1 của mảng
i = 0

while i < 100:
    i = i + 1

02,
i = 0

while i < 100:
    i = i + 1

07 chỉ vào tiêu đề của các nhóm có kích thước lớp
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1. Ví dụ,
i = 0

while i < 100:
    i = i + 1

09 chỉ vào tiêu đề của các nhóm có kích thước lớp
i = 0

while i < 100:
    i = i + 1

10 và
i = 0

while i < 100:
    i = i + 1

11 chỉ vào tiêu đề của các nhóm có kích thước lớp
i = 0

while i < 100:
    i = i + 1

12.

Hình dưới đây sẽ giúp dễ hiểu hơn:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Vì các nhóm có cùng lớp có cùng kích thước được liên kết với nhau bằng danh sách được liên kết gấp đôi, tất cả các nhóm trong trạng thái

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8 của mỗi lớp kích thước có thể được đi qua bằng cách sử dụng mảng
i = 0

while i < 100:
    i = i + 1

02.

Nếu

i = 0

while i < 100:
    i = i + 1

07 chỉ vào
i = 0

while i < 100:
    i = i + 1

16, điều đó có nghĩa là không có nhóm có kích thước lớp
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1 ở trạng thái
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8. Nếu một đối tượng yêu cầu một khối có kích thước lớp
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1, CPython sẽ khắc một nhóm mới có kích thước lớp
import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2
1 và cập nhật
i = 0

while i < 100:
    i = i + 1

07 để trỏ đến nhóm mới này.

Nếu một khối được giải phóng khỏi một nhóm ở trạng thái

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9, trạng thái hồ bơi sẽ thay đổi từ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9 thành
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8. Cpython thêm nhóm này vào mặt trước của danh sách liên kết gấp đôi của các nhóm thuộc lớp kích thước của nó.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Như đã thấy trong hình trên,

i = 0

while i < 100:
    i = i + 1

25 có kích thước lớp
i = 0

while i < 100:
    i = i + 1

10 và nó ở trạng thái
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9. Khi một khối được giải phóng khỏi
i = 0

while i < 100:
    i = i + 1

25, trạng thái của nó thay đổi từ
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9 thành
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8. Khi trạng thái của
i = 0

while i < 100:
    i = i + 1

25 trở thành
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8, CPython thêm nhóm này vào mặt trước của danh sách các nhóm có kích thước có kích thước
i = 0

while i < 100:
    i = i + 1

10 và
i = 0

while i < 100:
    i = i + 1

09 sẽ bắt đầu chỉ vào nhóm này.

Đấu trường

Đấu trường là các khối bộ nhớ lớn được sử dụng để phân bổ bộ nhớ cho các đối tượng nhỏ. Chúng được phân bổ bởi bộ phân bổ bộ nhớ thô với kích thước 256 kb.

Khi một đối tượng nhỏ yêu cầu bộ nhớ, nhưng không có đấu trường hiện có nào để xử lý yêu cầu này, thay vì chỉ yêu cầu bộ nhớ cho đối tượng nhỏ này, bộ phân bổ bộ nhớ thô yêu cầu một khối bộ nhớ lớn (256 kb) từ hệ điều hành. Những khối bộ nhớ lớn này được gọi là đấu trường.

Các hồ bơi (kích thước 4 kb) được chạm khắc từ các đấu trường khi cần thiết.

Hãy xem xét

i = 0

while i < 100:
    i = i + 1

35, như được định nghĩa trong cơ sở mã Cpython:

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};

Con trỏ

i = 0

while i < 100:
    i = i + 1

36 chỉ vào danh sách các nhóm miễn phí và các nhóm miễn phí không có bất kỳ khối nào được phân bổ.

Thuật ngữ

i = 0

while i < 100:
    i = i + 1

37 chỉ ra số lượng nhóm miễn phí trong đấu trường.

CPython duy trì một danh sách được liên kết gấp đôi có tên là

i = 0

while i < 100:
    i = i + 1

38 để theo dõi tất cả các đấu trường với các nhóm
i = 0

while i < 100:
    i = i + 1

39.

Các nhóm

i = 0

while i < 100:
    i = i + 1

40 ở trạng thái
import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2
0 hoặc
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
8.

Con trỏ

i = 0

while i < 100:
    i = i + 1

43 trỏ đến đấu trường có thể sử dụng tiếp theo, trong khi con trỏ
i = 0

while i < 100:
    i = i + 1

44 trỏ đến đấu trường có thể sử dụng trước đó trong danh sách liên kết gấp đôi
i = 0

while i < 100:
    i = i + 1

38. Danh sách liên kết gấp đôi được sắp xếp theo thứ tự ngày càng tăng của giá trị
i = 0

while i < 100:
    i = i + 1

37.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Như được hiển thị ở trên,

i = 0

while i < 100:
    i = i + 1

38 được sắp xếp trên cơ sở
i = 0

while i < 100:
    i = i + 1

37. Đấu trường với 0 nhóm miễn phí là mục đầu tiên, tiếp theo là đấu trường với 1 nhóm miễn phí, v.v. Nó có nghĩa là danh sách được sắp xếp với các đấu trường được phân bổ nhiều nhất trước tiên. Chúng tôi sẽ giải thích lý do tại sao việc sắp xếp này được nhập trong phần tiếp theo của bài viết.

Danh sách đấu trường có thể sử dụng được sắp xếp dựa trên những gì có phân bổ nhiều nhất, vì vậy khi có yêu cầu phân bổ bộ nhớ, nó được phục vụ từ đấu trường với nhiều phân bổ nhất.

Liệu một quá trình Python có phát hành bộ nhớ không?

Khi một khối được phân bổ trong một nhóm được giải phóng, CPython không đưa bộ nhớ trở lại hệ điều hành. Bộ nhớ này tiếp tục thuộc về quy trình Python và CPython sử dụng khối này để phân bổ bộ nhớ cho các đối tượng mới.

Ngay cả khi tất cả các khối trong một nhóm được giải phóng, CPython không trả lại bất kỳ bộ nhớ nào từ nhóm đến hệ điều hành. CPython giữ bộ nhớ của toàn bộ hồ bơi dành riêng cho việc sử dụng riêng.

CPYThon phát hành bộ nhớ trở lại hệ điều hành ở cấp độ của đấu trường, không phải ở cấp độ khối hoặc nhóm. Ngoài ra, xin lưu ý rằng Cpython phát hành bộ nhớ của toàn bộ đấu trường cùng một lúc.

Vì bộ nhớ chỉ có thể được phát hành ở cấp độ đấu trường, Cpython tạo ra đấu trường từ phần bộ nhớ mới chỉ khi nó hoàn toàn cần thiết! Nó luôn cố gắng phân bổ bộ nhớ từ các khối được chạm khắc và hồ bơi trước đó.

Đây là lý do

i = 0

while i < 100:
    i = i + 1

38 được sắp xếp theo thứ tự giảm của
i = 0

while i < 100:
    i = i + 1

37. Yêu cầu tiếp theo cho bộ nhớ sẽ được phân bổ từ các đấu trường với nhiều phân bổ nhất. Điều này cho phép các đấu trường có dữ liệu ít nhất trở nên miễn phí nếu các đối tượng chúng chứa bị xóa và bộ nhớ bị chiếm bởi các đấu trường này có thể được phát hành cho hệ điều hành.

Bộ sưu tập rác trong Python

Bộ sưu tập rác được định nghĩa là quá trình cải tạo hoặc phát hành bộ nhớ được phân bổ khi chương trình không còn cần thiết.

Bộ sưu tập rác (GC) là một hình thức quản lý bộ nhớ tự động. Người thu gom rác cố gắng đòi lại bộ nhớ được phân bổ bởi chương trình, nhưng không còn được tham chiếu nữa - còn được gọi là rác. - Wikipedia

Trong các ngôn ngữ như C, lập trình viên phải tự giải phóng bộ nhớ cho các đối tượng không sử dụng (bộ sưu tập rác của các đối tượng không sử dụng), trong khi bộ sưu tập rác trong Python được tự động chăm sóc bởi chính ngôn ngữ.

Python sử dụng hai phương pháp để thu thập rác tự động:

  1. Bộ sưu tập rác trên cơ sở đếm tham chiếu.
  2. Bộ sưu tập rác thế hệ.

Trước tiên hãy giải thích số lượng tham chiếu là gì, và sau đó chúng ta sẽ tìm hiểu thêm về bộ sưu tập rác trên cơ sở đếm tham chiếu!

Số lượng tham chiếu

Như chúng ta đã thấy trước đó, CPython nội bộ tạo ra các thuộc tính

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
4 và
i = 0

while i < 100:
    i = i + 1

52 cho mỗi đối tượng.

Hãy xem xét một ví dụ để hiểu rõ hơn về thuộc tính

i = 0

while i < 100:
    i = i + 1

52:

Khi mã trên được thực thi, CPython sẽ tạo một đối tượng

i = 0

while i < 100:
    i = i + 1

54 loại
i = 0

while i < 100:
    i = i + 1

55, như được hiển thị bên dưới:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Trường

i = 0

while i < 100:
    i = i + 1

52 cho biết số lượng tham chiếu đến đối tượng. Chúng ta biết rằng trong Python, các biến chỉ là các tham chiếu đến các đối tượng. Trong ví dụ trên, biến
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 là tham chiếu duy nhất cho đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54. Do đó, giá trị
i = 0

while i < 100:
    i = i + 1

52 của đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 là
i = 0

while i < 100:
    i = i + 1

12.

Chúng ta có thể nhận được số lượng tham chiếu của bất kỳ đối tượng nào trong Python bằng phương pháp

i = 0

while i < 100:
    i = i + 1

62.

Chúng ta hãy lấy số lượng tham chiếu của đối tượng chuỗi

i = 0

while i < 100:
    i = i + 1

54:

import sys

ref_count = sys.getrefcount(a)

print(ref_count)   # Output: 2

Đầu ra của mã trên là

i = 0

while i < 100:
    i = i + 1

64. Điều này chỉ ra rằng đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 đang được tham chiếu bởi hai biến. Tuy nhiên, chúng tôi đã thấy trước đó rằng đối tượng
i = 0

while i < 100:
    i = i + 1

54 chỉ được tham chiếu bởi biến
/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8.

Tại sao giá trị đếm tham chiếu

i = 0

while i < 100:
    i = i + 1

64 cho đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 khi sử dụng phương thức
i = 0

while i < 100:
    i = i + 1

62?

Để hiểu điều này, hãy để xem xét định nghĩa của phương pháp

i = 0

while i < 100:
    i = i + 1

62:

def getrefcount(var):
    ...

Lưu ý: Định nghĩa

i = 0

while i < 100:
    i = i + 1

62 ở trên chỉ dành cho mục đích giải thích.

Ở đây, khi chúng ta chuyển biến

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 cho phương thức
i = 0

while i < 100:
    i = i + 1

62, đối tượng
i = 0

while i < 100:
    i = i + 1

54 cũng được đề cập bởi tham số
i = 0

while i < 100:
    i = i + 1

76 của phương thức
i = 0

while i < 100:
    i = i + 1

62. Do đó, số lượng tham chiếu của đối tượng
i = 0

while i < 100:
    i = i + 1

54 là
i = 0

while i < 100:
    i = i + 1

64.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Do đó, bất cứ khi nào chúng tôi sử dụng phương thức

i = 0

while i < 100:
    i = i + 1

62 để lấy số lượng tham chiếu của một đối tượng, số lượng tham chiếu sẽ luôn luôn nhiều hơn 1 so với số lượng tham chiếu thực tế của đối tượng.

Hãy tạo một biến khác,

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0, chỉ vào cùng một đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Các biến

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
8 và
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 đều chỉ vào đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54. Do đó, số lượng tham chiếu của đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 sẽ là 2 và đầu ra của phương thức
i = 0

while i < 100:
    i = i + 1

62 sẽ là 3.

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 3

Giảm số lượng tham chiếu

Để giảm số lượng tham chiếu, chúng tôi phải xóa các tham chiếu đến biến. Đây là một ví dụ:

Biến

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 không còn trỏ đến đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54. Do đó, số lượng tham chiếu của đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 cũng sẽ giảm.

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2

Giảm số lượng tham chiếu bằng cách sử dụng từ khóa i = 0 while i < 100: i = i + 1 91

Chúng tôi cũng có thể sử dụng từ khóa

i = 0

while i < 100:
    i = i + 1

91 để giảm số lượng tham chiếu của một đối tượng.

Nếu chúng ta gán

i = 0

while i < 100:
    i = i + 1

93 cho biến
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 (
i = 0

while i < 100:
    i = i + 1

95),
struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 không còn trỏ đến đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54. Từ khóa
i = 0

while i < 100:
    i = i + 1

91 hoạt động theo cùng một cách và được sử dụng để xóa tham chiếu của đối tượng, do đó giảm số lượng tham chiếu của nó.

Xin lưu ý rằng từ khóa

i = 0

while i < 100:
    i = i + 1

91 không xóa đối tượng.

Xem xét ví dụ sau:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Ở đây, chúng tôi chỉ đang xóa tham chiếu của

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0. Đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 không bị xóa.

Bây giờ chúng ta hãy lấy số lượng tham chiếu của đối tượng chuỗi

i = 0

while i < 100:
    i = i + 1

54:

import sys

ref_count = getrefcount(b)

print(ref_count) # Output: 2

Hoàn hảo! Số lượng tham chiếu của

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 bây giờ là 2.

Hãy tóm tắt lại những gì chúng ta đã học về số lượng tham chiếu!

Số lượng tham chiếu của một đối tượng tăng nếu chúng ta gán cùng một đối tượng cho một biến mới. Khi chúng ta phân định đối tượng bằng cách sử dụng từ khóa

i = 0

while i < 100:
    i = i + 1

91 hoặc bằng cách làm cho nó trỏ đến
i = 0

while i < 100:
    i = i + 1

93, số lượng tham chiếu của đối tượng đó sẽ giảm.

Bây giờ chúng ta đã hiểu rõ hơn về khái niệm số lượng tham chiếu trong Python, hãy tìm hiểu cách thu gom rác hoạt động trên cơ sở số lượng tham chiếu.

Bộ sưu tập rác trên cơ sở số lượng tham chiếu

Bộ sưu tập rác trên cơ sở số lượng tham chiếu sử dụng số lượng tham chiếu của đối tượng để giải phóng hoặc lấy lại bộ nhớ. Khi số lượng tham chiếu của đối tượng bằng 0, bộ sưu tập rác của Python sẽ vào và loại bỏ đối tượng khỏi bộ nhớ.

Khi một đối tượng bị xóa khỏi bộ nhớ, nó có thể kích hoạt việc xóa các đối tượng khác.

Xem xét ví dụ sau:

i = 0

while i < 100:
    i = i + 1

0

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Ở đây, chúng tôi chỉ đang xóa tham chiếu của

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0. Đối tượng chuỗi
i = 0

while i < 100:
    i = i + 1

54 không bị xóa.

Bây giờ chúng ta hãy lấy số lượng tham chiếu của đối tượng chuỗi

i = 0

while i < 100:
    i = i + 1

54:

Hoàn hảo! Số lượng tham chiếu của

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 bây giờ là 2.

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Hãy tóm tắt lại những gì chúng ta đã học về số lượng tham chiếu!

Số lượng tham chiếu của một đối tượng tăng nếu chúng ta gán cùng một đối tượng cho một biến mới. Khi chúng ta phân định đối tượng bằng cách sử dụng từ khóa

i = 0

while i < 100:
    i = i + 1

91 hoặc bằng cách làm cho nó trỏ đến
i = 0

while i < 100:
    i = i + 1

93, số lượng tham chiếu của đối tượng đó sẽ giảm.

Bây giờ chúng ta đã hiểu rõ hơn về khái niệm số lượng tham chiếu trong Python, hãy tìm hiểu cách thu gom rác hoạt động trên cơ sở số lượng tham chiếu.

Bộ sưu tập rác trên cơ sở số lượng tham chiếu

Bộ sưu tập rác dựa trên số lượng tham chiếu là thời gian thực!

Bộ sưu tập rác được kích hoạt ngay khi số lượng tham chiếu của đối tượng trở thành 0. Đây là thuật toán thu gom rác chính của Python, và do đó, nó không thể bị vô hiệu hóa.

Bộ sưu tập rác trên cơ sở số lượng tham chiếu không hoạt động nếu có tài liệu tham khảo theo chu kỳ. Để miễn phí hoặc đòi lại bộ nhớ của các đối tượng có tài liệu tham khảo theo chu kỳ, Python sử dụng thuật toán thu thập rác thế hệ.

Tiếp theo, chúng tôi sẽ thảo luận về các tài liệu tham khảo theo chu kỳ và sau đó đi sâu hơn để hiểu thêm về thuật toán Bộ sưu tập rác thế hệ!

Tài liệu tham khảo theo chu kỳ trong Python

Một tham chiếu theo chu kỳ hoặc tham chiếu tròn là một điều kiện trong đó một đối tượng tự tham khảo hoặc khi hai đối tượng khác nhau đề cập đến nhau.

Tài liệu tham khảo theo chu kỳ chỉ có thể với các đối tượng container, chẳng hạn như danh sách, dict và đối tượng do người dùng xác định. Không thể với các loại dữ liệu bất biến, chẳng hạn như số nguyên, phao hoặc chuỗi.

Xem xét ví dụ sau:

i = 0

while i < 100:
    i = i + 1

1

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Như đã thấy trong biểu diễn trên, đối tượng mảng

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0 đang tự tham khảo.

Hãy xóa tham chiếu

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0:

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Khi chúng tôi xóa tham chiếu

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};
0, đối tượng mảng sẽ không thể truy cập được từ mã Python, nhưng nó sẽ tiếp tục tồn tại trong bộ nhớ, như trong hình trên.

Vì đối tượng mảng đang tham chiếu, số lượng tham chiếu của đối tượng mảng sẽ không bao giờ trở thành 0 và nó sẽ không bao giờ được thu thập bởi bộ thu gom rác tham chiếu.

Hãy xem xét một ví dụ khác:

i = 0

while i < 100:
    i = i + 1

2

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Ở đây, chúng tôi đã xác định hai đối tượng,

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26, của các lớp
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
27 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
28, tương ứng.
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25 được đề cập bởi biến
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
30 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26 được đề cập bởi biến
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
32.

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25 có một tài sản
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
34 chỉ vào
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26. Tương tự, đối tượng
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26 có thuộc tính
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
37 chỉ vào
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25.

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26 có thể được truy cập trong mã Python với sự trợ giúp của các biến
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
30 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
32, tương ứng.

Hãy xóa các biến

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
30 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
32

Hướng dẫn why is python automatic memory management? - tại sao python quản lý bộ nhớ tự động?

Khi chúng ta xóa các biến

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
30 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
32,
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
25 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
26 sẽ không thể truy cập được từ cơ sở mã Python, nhưng chúng sẽ tiếp tục tồn tại trong bộ nhớ.

Số lượng tham chiếu của các đối tượng này sẽ không bao giờ trở thành 0, vì chúng chỉ vào nhau (theo thuộc tính

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
37 và
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
34). Do đó, những đối tượng này sẽ không bao giờ được thu thập bởi bộ thu gom rác tham chiếu.

Vì số lượng tham chiếu của các đối tượng có tham chiếu theo chu kỳ không bao giờ trở thành 0, phương thức thu thập rác tham chiếu sẽ không bao giờ làm sạch các đối tượng này. Đối với những trường hợp như vậy, Python cung cấp một thuật toán thu gom rác khác có tên là Bộ sưu tập rác thế hệ.

Bộ sưu tập rác thế hệ trong Python

Thuật toán thu thập rác thế hệ giải quyết vấn đề thu thập rác đối tượng có tài liệu tham khảo tròn. Vì các tham chiếu tròn chỉ có thể với các đối tượng container, nó quét tất cả các đối tượng container, phát hiện các đối tượng có tham chiếu tròn và loại bỏ chúng nếu chúng có thể được thu thập rác.

Để giảm số lượng đối tượng được quét, bộ sưu tập rác thế hệ cũng bỏ qua các bộ dữ liệu chỉ chứa các loại bất biến (ví dụ: int và chuỗi).

Vì các đối tượng quét và phát hiện các chu kỳ là một quá trình tốn thời gian, thuật toán thu thập rác thế hệ không phải là thời gian thực. Nó được kích hoạt định kỳ. Khi thuật toán thu gom rác thế hệ được kích hoạt, mọi thứ khác đều bị dừng lại. Do đó, để giảm số lần thu gom rác thế hệ được kích hoạt, CPython phân loại các đối tượng container thành nhiều thế hệ và xác định ngưỡng cho từng thế hệ này. Bộ sưu tập rác thế hệ được kích hoạt nếu số lượng đối tượng trong một thế hệ nhất định vượt quá ngưỡng xác định.

CPYThon phân loại các đối tượng thành ba thế hệ (thế hệ 0, 1 và 2). Khi một đối tượng mới được tạo ra, nó thuộc về thế hệ đầu tiên. Nếu đối tượng này không được thu thập khi CPython chạy thuật toán thu gom rác thế hệ, thì nó sẽ chuyển sang thế hệ thứ hai. Nếu đối tượng không được thu thập khi CPython chạy bộ sưu tập rác thế hệ một lần nữa, nó sẽ được chuyển sang thế hệ thứ ba. Đây là thế hệ cuối cùng, và đối tượng sẽ vẫn ở đó.

Nói chung, hầu hết các đối tượng được thu thập trong thế hệ đầu tiên.

Khi bộ sưu tập rác thế hệ được kích hoạt cho một thế hệ nhất định, nó cũng thu thập tất cả các thế hệ trẻ. Ví dụ: nếu bộ sưu tập rác được kích hoạt cho thế hệ 1, nó cũng sẽ thu thập các đối tượng có trong Thế hệ 0.

Tình huống mà cả ba thế hệ (0, 1 và 2) được thu thập rác được gọi là bộ sưu tập đầy đủ. Vì bộ sưu tập

import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9 liên quan đến việc quét và phát hiện các chu kỳ trong một số lượng lớn các đối tượng, Cpython cố gắng tránh thu thập
import sys

ref_count = getrefcount(a)

print(ref_count) # Output: 2
9 càng nhiều càng tốt.

Chúng ta có thể thay đổi hành vi của người thu gom rác thế hệ bằng mô -đun

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
0 do Python cung cấp. Bộ sưu tập rác thế hệ có thể được vô hiệu hóa bằng mô -đun
i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
0. Do đó, nó còn được gọi là bộ sưu tập rác tùy chọn.

Tiếp theo, chúng tôi sẽ giải thích một số phương pháp quan trọng của mô -đun

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
0.

i = 0 # malloc(i) while i < 100: # malloc(i + 1) # free(i) i = i + 1 0 Mô -đun trong Python

Chúng ta có thể sử dụng phương pháp sau để có được ngưỡng được xác định cho mỗi thế hệ:

i = 0

while i < 100:
    i = i + 1

3

Đầu ra trên chỉ ra rằng ngưỡng cho thế hệ đầu tiên là 700, trong khi nó là 10 cho thế hệ thứ hai và thứ ba.

Nếu có hơn 700 đối tượng trong thế hệ đầu tiên, bộ sưu tập rác thế hệ sẽ được kích hoạt cho tất cả các đối tượng trong thế hệ đầu tiên. Nếu có hơn 10 đối tượng trong thế hệ thứ 2, bộ sưu tập rác thế hệ sẽ được kích hoạt cho cả hai thế hệ 1 và 0.

Chúng ta cũng có thể kiểm tra số lượng đối tượng trong mỗi thế hệ, như được hiển thị bên dưới:

i = 0

while i < 100:
    i = i + 1

4

Ở đây, số lượng đối tượng trong thế hệ đầu tiên là 679, trong khi số lượng đối tượng trong thế hệ thứ hai là 8 và số lượng đối tượng trong thế hệ thứ ba là 0.

Chúng ta cũng có thể chạy thuật toán thu thập rác thế hệ theo cách thủ công, như hình dưới đây:

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1
57 sẽ kích hoạt bộ sưu tập rác thế hệ. Theo mặc định, nó chạy một bộ sưu tập đầy đủ. Để chạy bộ sưu tập rác thế hệ cho thế hệ đầu tiên, chúng ta có thể gọi phương pháp này như được hiển thị bên dưới:

i = 0

while i < 100:
    i = i + 1

5

Hãy kiểm tra số lượng đối tượng vẫn còn sống sau quá trình thu thập,

i = 0

while i < 100:
    i = i + 1

6

Chúng tôi cũng có thể cập nhật ngưỡng thế hệ cho từng thế hệ, như được hiển thị bên dưới:

i = 0

while i < 100:
    i = i + 1

7

Chúng ta có thể vô hiệu hóa bộ sưu tập rác thế hệ bằng lệnh sau:

Ngược lại, chúng ta có thể bật lại bằng cách sử dụng lệnh sau:

Sự kết luận

Trong bài viết này, chúng tôi đã học được rằng Python là một tập hợp các quy tắc và thông số kỹ thuật, và CPython là việc triển khai tham chiếu của Python trong C. Phân bổ và phân bổ mục đích chung, mà Python sử dụng để quản lý bộ nhớ hiệu quả. Sau đó, chúng tôi đã tìm hiểu về các đấu trường, nhóm và các khối mà Trình quản lý bộ nhớ Python sử dụng để tối ưu hóa phân bổ bộ nhớ/phân bổ cho các đối tượng nhỏ (nhỏ hơn hoặc bằng kích thước byte 512 byte) trong một chương trình. Chúng tôi cũng đã tìm hiểu về các thuật toán thu gom rác, chẳng hạn như đếm tham chiếu và thu gom rác thế hệ.

Người giới thiệu

  • Tài liệu quản lý bộ nhớ Python
  • OBMALLOC.C Triển khai trong cơ sở mã CPython
  • Quản lý bộ nhớ trong Blog Python trên Summer
  • Trình phân bổ bộ nhớ của Python
  • Bộ sưu tập rác trong Python
  • Người thu gom rác

Là quản lý bộ nhớ tự động Python?

Lập trình viên phải phân bổ thủ công bộ nhớ trước khi chương trình có thể sử dụng và phát hành nó khi chương trình không còn cần nó nữa. Trong Python, quản lý bộ nhớ là tự động! Python tự động xử lý việc phân bổ và giải quyết bộ nhớ.In Python, memory management is automatic! Python automatically handles the allocation and deallocation of memory.

Tại sao Python sử dụng nhiều bộ nhớ như vậy?

Những con số đó có thể dễ dàng phù hợp với số nguyên 64 bit, vì vậy người ta sẽ hy vọng Python sẽ lưu trữ hàng triệu số nguyên đó không quá ~ 8MB: một triệu đối tượng 8 byte.Trên thực tế, Python sử dụng nhiều RAM hơn 35 MB để lưu trữ các số này.Tại sao?Bởi vì số nguyên python là đối tượng và các đối tượng có nhiều bộ nhớ trên đầu.Because Python integers are objects, and objects have a lot of memory overhead.

Quản lý bộ nhớ tự động là gì?

Quản lý bộ nhớ tự động là một trong những dịch vụ mà thời gian chạy ngôn ngữ chung cung cấp trong quá trình thực hiện được quản lý.Trình thu gom rác của ngôn ngữ chung của ngôn ngữ quản lý việc phân bổ và phát hành bộ nhớ cho một ứng dụng.one of the services that the Common Language Runtime provides during Managed Execution. The Common Language Runtime's garbage collector manages the allocation and release of memory for an application.

Làm thế nào để Python giải phóng bộ nhớ?

Như đã giải thích trước đó, Python xóa các đối tượng không còn được tham chiếu trong chương trình để giải phóng không gian bộ nhớ.Quá trình này trong đó Python giải phóng các khối bộ nhớ không còn được sử dụng được gọi là bộ sưu tập rác.deletes objects that are no longer referenced in the program to free up memory space. This process in which Python frees blocks of memory that are no longer used is called Garbage Collection.