11. Bộ cấp phát bộ nhớ
11. Bộ cấp phát bộ nhớ
CPython phân bổ bộ nhớ liên tục. Mọi đối tượng số nguyên, đối tượng danh sách, khung, bộ dữ liệu, mảng nhập lệnh, bộ đệm chuỗi, đối tượng mã, ngoại lệ, mô-đun và hàm đều cần bộ nhớ. Hệ thống phân bổ tồn tại để thực hiện các phân bổ này nhanh chóng, có cấu trúc, có thể sửa lỗi và di động trên các nền tảng.
CPython không chỉ sử dụng một bộ cấp phát. Nó sử dụng một số miền và lớp cấp phát. Các đối tượng Python nhỏ thường đi qua bộ cấp phát đối tượng nhỏ chuyên dụng của CPython, trong khi các bộ đệm lớn hơn có thể đi qua bộ cấp phát nền tảng.
11.1 Tại sao CPython có bộ cấp phát riêng
Một chương trình Python tạo ra nhiều đối tượng có thời gian tồn tại ngắn.python for i in range(1_000_000): x = (i, i + 1) Vòng lặp này phân bổ nhiều đối tượng bộ và tham chiếu số nguyên. Nếu mọi phân bổ đối tượng nhỏ đều đi thẳng vào hệ thốngmalloc, chi phí sẽ cao.
Hệ thống cấp phát của CPython cải thiện điều này bằng cách:```text serving small object allocations quickly grouping small allocations into arenas and pools reducing calls into the platform allocator supporting debug hooks separating allocator domains making object allocation behavior predictable enough for internals work
## 11.2 Miền cấp phát
CPython phân bổ bộ nhớ thành các miền.
Các miền quan trọng là:
| Tên miền | Sử dụng điển hình |
| ------------- | --------------------------------------------------- |
| Bộ nhớ thô | Bộ nhớ cấp thấp độc lập với trạng thái đối tượng Python |
| Ký ức | Bộ nhớ thời gian chạy Python đa năng |
| Bộ nhớ đối tượng | Phân bổ đối tượng Python |
Ở cấp độ API C, chúng xuất hiện dưới dạng họ hàm:```c
PyMem_RawMalloc
PyMem_RawCalloc
PyMem_RawRealloc
PyMem_RawFree
PyMem_Malloc
PyMem_Calloc
PyMem_Realloc
PyMem_Free
PyObject_Malloc
PyObject_Calloc
PyObject_Realloc
PyObject_Free
```Sự khác biệt rất quan trọng vì mỗi miền có thể có các hook, ràng buộc và hành vi gỡ lỗi khác nhau.
Một quy tắc đơn giản:```text
PyMem_Raw*
use for memory that may be allocated without an initialized Python runtime
PyMem_*
use for general Python memory
PyObject_*
use for memory belonging to Python objects
```Mã mở rộng phải khớp với các hàm phân bổ và hàm tự do trong cùng một họ.
Chính xác:```c
char *p = PyMem_Malloc(128);
if (p == NULL) {
return PyErr_NoMemory();
}
/* use p */
PyMem_Free(p);
```Không đúng:```c
char *p = PyMem_Malloc(128);
free(p); /* wrong allocator family */
```Trộn các họ cấp phát có thể làm hỏng bộ nhớ.
## 11.3 Phân bổ đối tượng và khởi tạo đối tượng
Phân bổ dự trữ bộ nhớ. Việc khởi tạo cung cấp cho bộ nhớ đó một trạng thái đối tượng hợp lệ.
Đối với các đối tượng Python, sự khác biệt này rất quan trọng.
Phân bổ đối tượng:```text
reserve memory for object layout
set object header
set type pointer
set reference count
possibly track with GC
```Khởi tạo đối tượng:```text
fill fields
store references
validate arguments
establish invariants
```Đối với lớp do người dùng định nghĩa:```python
obj = MyClass(1, 2)
```quá trình thô là:```text
call type machinery
allocate memory for instance
initialize object header
call __new__
call __init__
return initialized object
```Đối với các loại tiện ích mở rộng C, việc phân bổ thường đi qua đối tượng loại:```c
self = (MyObject *)type->tp_alloc(type, 0);
```Sau đó khởi tạo điền vào các trường.
## 11.4`tp_alloc`Và`tp_free`Mọi đối tượng loại có thể chỉ định cách phân bổ và giải phóng các thể hiện.
Khe cắm loại quan trọng:```text
tp_alloc
tp_free
tp_dealloc
tp_allocdự trữ bộ nhớ cho một đối tượng mới.tp_freegiải phóng bộ nhớ cho đối tượng.tp_dealloclà hàm hủy dành riêng cho loại. Nó thường giải phóng các trường và sau đó gọitp_free.
Hình dạng đơn giản:```c static void MyObject_dealloc(MyObject *self) { Py_XDECREF(self->value); Py_TYPE(self)->tp_free((PyObject *)self); }
Sự tách biệt này cho phép các loại đối tượng khác nhau sử dụng các chiến lược phân bổ khác nhau trong khi vẫn duy trì việc dọn dẹp theo từng loại cụ thể một cách rõ ràng.
## 11.5 Bộ phân bổ đối tượng nhỏ
Bộ cấp phát đối tượng nhỏ của CPython thường được gọi là`pymalloc`.
Nó được tối ưu hóa cho các khối bộ nhớ nhỏ được sử dụng bởi các đối tượng Python.
Về mặt khái niệm:```text
arena
large region obtained from system allocator
pool
fixed-size subdivision inside an arena
block
one small allocation served to CPython
```Hệ thống phân cấp:```text
system allocator
↓
arenas
↓
pools
↓
blocks
```Phân bổ nhỏ được làm tròn thành các lớp kích thước. Một nhóm phục vụ các khối có cùng kích thước.
Điều này tránh việc yêu cầu người cấp phát hệ thống cho từng đối tượng nhỏ.
## 11.6 Đấu trường
Một đấu trường là một vùng bộ nhớ lớn thu được từ bộ cấp phát cơ bản.
Về mặt khái niệm:```text
arena
pool
pool
pool
...
```Đấu trường cho phép CPython quản lý nhiều việc phân bổ đối tượng nhỏ theo đợt.
Khi CPython cần thêm bộ nhớ cho các đối tượng nhỏ, nó sẽ yêu cầu một đấu trường. Đấu trường đó được chia thành các hồ bơi. Bể bơi được sử dụng để phục vụ các khối.
Một đấu trường chỉ có thể được đưa trở lại hệ thống khi tất cả các nhóm bên trong nó trở nên miễn phí. Điều này có nghĩa là bộ nhớ có thể vẫn được CPython dành riêng ngay cả sau khi nhiều đối tượng bị phá hủy.
Hành vi đó có thể khiến người dùng ngạc nhiên:```text
objects were freed
process RSS did not immediately shrink
```Điều này không phải lúc nào cũng chỉ ra sự rò rỉ. Bộ nhớ có thể được bộ cấp phát giữ để tái sử dụng.
## 11.7 Bể bơi
Bể bơi là một phần của đấu trường.
Mỗi nhóm phục vụ một lớp kích thước khối tại một thời điểm.
Ví dụ:```text
pool A
32-byte blocks
pool B
64-byte blocks
pool C
128-byte blocks
```Khi một nhóm được gán cho một lớp kích thước, tất cả các khối trong nhóm đó có cùng kích thước. Điều này làm cho việc phân bổ và hoạt động tự do trở nên đơn giản.
Bộ cấp phát có thể duy trì danh sách các nhóm có các khối có sẵn. Việc phân bổ một đối tượng nhỏ thường có nghĩa là lấy khối có sẵn tiếp theo từ một nhóm.
## 11.8 Khối và lớp kích thước
Một khối là bộ nhớ được trả về cho một yêu cầu cấp phát.
Kích thước phân bổ nhỏ được làm tròn thành các lớp kích thước.
Khái niệm ví dụ:```text
request 37 bytes
rounded to 40 or 48 byte class depending on allocator rules
request 72 bytes
rounded to matching size class
request too large
bypass pymalloc and use larger allocator path
```Các lớp kích thước chính xác phụ thuộc vào phiên bản CPython và cấu hình bản dựng.
Ý tưởng quan trọng:```text
small requests use fixed-size pools
large requests use another allocator path
```Nhóm kích thước cố định giúp phân bổ nhanh hơn và giảm phân mảnh bên trong khối lượng công việc đối tượng nhỏ.
## 11.9 Danh sách miễn phí
Một số loại đối tượng sử dụng danh sách miễn phí ngoài bộ cấp phát chung.
Danh sách miễn phí lưu trữ các đối tượng đã bị phá hủy gần đây thuộc một loại cụ thể để chúng có thể được sử dụng lại nhanh chóng.
Các ví dụ phổ biến trong lịch sử CPython bao gồm các khung, bộ dữ liệu có kích thước nhất định, số float, danh sách và các đối tượng nội bộ khác, mặc dù việc sử dụng danh sách miễn phí chính xác thay đổi theo phiên bản.
Dòng khái niệm:```text
destroy object
if type-specific free list has room:
put object memory on free list
else:
return memory to allocator
create object
if free list has cached object:
reuse it
else:
allocate new memory
```Danh sách miễn phí đánh đổi khả năng giữ bộ nhớ để lấy tốc độ.
Chúng có thể thực hiện việc phân bổ đối tượng nhanh hơn nhiều trong các vòng lặp chặt chẽ, nhưng chúng cũng có nghĩa là các đối tượng được giải phóng có thể không trả lại bộ nhớ ngay lập tức cho bộ cấp phát.
## 11.10 Thực tập và tái sử dụng đối tượng
Một số đối tượng được tái sử dụng có chủ ý.
Ví dụ bao gồm:```text
None
True
False
small integers
some strings
empty tuple
interned identifiers
```Việc tái sử dụng này làm giảm áp lực phân bổ và cho phép so sánh nhanh hơn trong một số đường dẫn nội bộ.
Ví dụ:```python
a = "name"
b = "name"
```Tùy thuộc vào cách tạo chuỗi, CPython có thể thực hiện chúng. Chuỗi nội bộ rất hữu ích cho các mã định danh, tên thuộc tính và khóa từ điển được sử dụng nội bộ.
Tái sử dụng đối tượng là một sự tối ưu hóa. Mã Python không nên dựa vào nhận dạng đối tượng ngoại trừ các tài liệu đơn lẻ như`None`, `True`, `False`, `NotImplemented`, Và`Ellipsis`.
Chính xác:```python
if value is None:
...
```Tránh xa:```python
if x is 1000:
...
```Thứ hai dựa vào hành vi tái sử dụng đối tượng cụ thể khi triển khai.
## 11.11 Đối tượng bất tử và phân bổ
CPython hiện đại sử dụng các đối tượng bất tử cho các đối tượng thuộc sở hữu thời gian chạy được chọn.
Một vật thể bất tử được coi là sống vĩnh viễn. Hoạt động đếm tham chiếu có thể tránh được các hiệu ứng thông thường suốt đời cho nó.
Điều này ảnh hưởng gián tiếp đến việc phân bổ:```text
some fundamental objects are allocated once
their lifetime is the runtime lifetime
normal deallocation never frees them
```Các ví dụ có thể bao gồm các đối tượng giống như singleton và các hằng số bên trong được sử dụng lại nhiều.
Đối với tác giả tiện ích mở rộng, quy tắc không thay đổi:```text
use Py_INCREF and Py_DECREF
do not manually inspect or change ob_refcnt
do not assume ordinary deallocation for every object
```Mã đúng sẽ hoạt động cho dù đối tượng là phàm nhân hay bất tử.
## 11.12 Phân mảnh bộ nhớ
Phân mảnh bộ nhớ xảy ra khi bộ nhớ trống tồn tại nhưng bị chia thành nhiều phần không thể đáp ứng các yêu cầu phân bổ lớn hơn hoặc không thể trả lại hệ điều hành một cách sạch sẽ.
CPython có thể bị phân mảnh ở nhiều cấp độ:```text
inside pymalloc pools
inside arenas
inside the system allocator
inside type-specific free lists
inside long-lived Python containers
```Mẫu ví dụ:```python
items = []
for i in range(1_000_000):
items.append(bytearray(100))
del items
```Các đối tượng Python có thể bị hủy, nhưng hành vi bộ nhớ phụ thuộc vào kích thước đối tượng, đường dẫn cấp phát, mức độ đầy đủ của trường, danh sách trống và hành vi cấp phát hệ thống.
RSS có thể duy trì ở mức cao vì CPython dự kiến sẽ sử dụng lại bộ nhớ sau này.
## 11.13`tracemalloc`
`tracemalloc`theo dõi phân bổ bộ nhớ Python.
Ví dụ:```python
import tracemalloc
tracemalloc.start()
data = [str(i) for i in range(100_000)]
current, peak = tracemalloc.get_traced_memory()
print(current, peak)
tracemalloc.stop()
```Nó có thể hiển thị nơi bộ nhớ được phân bổ:```python
import tracemalloc
tracemalloc.start()
data = [bytes(1024) for _ in range(1000)]
snapshot = tracemalloc.take_snapshot()
stats = snapshot.statistics("lineno")
for stat in stats[:10]:
print(stat)
tracemallocrất hữu ích cho việc gỡ lỗi phân bổ cấp độ Python. Nó không hiển thị mọi phân bổ gốc được thực hiện bởi mọi thư viện C.
11.14 Móc phân bổ gỡ lỗi
CPython hỗ trợ các hook bộ nhớ gỡ lỗi giúp phát hiện việc sử dụng sai mục đích cấp phát.
Bản dựng gỡ lỗi và chế độ cấp phát gỡ lỗi có thể phát hiện các sự cố như:```text writing before allocated memory writing after allocated memory using memory after free freeing memory with wrong allocator family double free uninitialized memory patterns
Móc gỡ lỗi thường thêm các byte đệm xung quanh phân bổ và lấp đầy bộ nhớ bằng các mẫu byte có thể nhận dạng được.
Điều này làm cho việc phát hiện lỗi bộ nhớ ở gần nguồn dễ dàng hơn.
## 11.15 Kỷ luật gia đình người phân bổ
Kỷ luật gia đình Allocator rất nghiêm khắc.
Các cặp đúng:
| Phân bổ | Miễn phí |
| ----------------- | --------------- |
|`PyMem_RawMalloc` | `PyMem_RawFree` |
| `PyMem_Malloc` | `PyMem_Free` |
| `PyObject_Malloc` | `PyObject_Free` |
| `malloc` | `free`|
Ghép nối không chính xác là lỗi:```c
void *p = PyObject_Malloc(64);
PyMem_Free(p); /* wrong */
```Cũng sai:```c
void *p = malloc(64);
PyObject_Free(p); /* wrong */
```Bộ cấp phát tạo bộ nhớ phải là bộ cấp phát giải phóng bộ nhớ.
## 11.16 Phân bổ bộ đệm trong mã mở rộng
Đối với các bộ đệm không phải đối tượng, hãy ưu tiên họ cấp phát Python thích hợp.
Ví dụ:```c
typedef struct {
PyObject_HEAD
char *data;
Py_ssize_t size;
} BufferObject;
```Phân bổ:```c
self->data = PyMem_Malloc(size);
if (self->data == NULL) {
PyErr_NoMemory();
return -1;
}
self->size = size;
```Phân bổ:```c
static void
Buffer_dealloc(BufferObject *self)
{
PyMem_Free(self->data);
Py_TYPE(self)->tp_free((PyObject *)self);
}
```Bộ đệm sử dụng`PyMem_*`. Bản thân đối tượng sử dụng kiểu`tp_alloc`Và`tp_free`.
Hãy giữ những kiếp sống này riêng biệt.
## 11.17 Bộ nhớ đối tượng và các tài liệu tham khảo có chứa
Việc cấp phát bộ nhớ đối tượng không tự động quản lý các tham chiếu Python có trong đó.
Ví dụ:```c
typedef struct {
PyObject_HEAD
PyObject *value;
} BoxObject;
```Phân bổ cung cấp bộ nhớ cho`value`, nhưng nó chưa sở hữu một tài liệu tham khảo hợp lệ.
Việc khởi tạo phải thiết lập nó một cách an toàn:```c
self->value = NULL;
```Sau đó gán tài liệu tham khảo sở hữu với`Py_INCREF`hoặc bằng cách nhận được tài liệu tham khảo bị đánh cắp/mới theo hợp đồng API.
Deallocation phải phát hành tham chiếu sở hữu:```c
Py_XDECREF(self->value);
```Cấp phát bộ nhớ và quyền sở hữu tham chiếu là các hệ thống có liên quan nhưng riêng biệt.
## 11.18 Lỗi phân bổ
Bộ phân bổ có thể thất bại.
Mã mở rộng C phải kiểm tra kết quả phân bổ.```c
void *p = PyMem_Malloc(size);
if (p == NULL) {
PyErr_NoMemory();
return NULL;
}
```Đối với các hàm tạo đối tượng,`NULL`thường có nghĩa là một ngoại lệ đã được đặt hoặc phải được đặt.
Đúng mẫu:```c
PyObject *obj = PyLong_FromLong(42);
if (obj == NULL) {
return NULL;
}
```Đừng bao giờ cho rằng việc phân bổ thành công. Mã Python có thể chạy dưới áp lực bộ nhớ, môi trường nhúng, vùng chứa bị hạn chế hoặc kiểm tra làm mờ.
## 11.19 Tái phân bổ`PyMem_Realloc`thay đổi kích thước của khối bộ nhớ.
Mẫu:```c
char *new_data = PyMem_Realloc(self->data, new_size);
if (new_data == NULL) {
PyErr_NoMemory();
return -1;
}
self->data = new_data;
self->size = new_size;
```Không ghi đè lên con trỏ ban đầu trước khi kiểm tra thành công.
Không đúng:```c
self->data = PyMem_Realloc(self->data, new_size);
if (self->data == NULL) {
return -1; /* old pointer lost */
}
```Nếu việc phân bổ lại không thành công, việc phân bổ ban đầu vẫn hợp lệ. Mất con trỏ đó sẽ làm rò rỉ bộ nhớ.
## 11.20 Phân bổ quá mức
Một số vùng chứa phân bổ quá mức để tránh phân bổ lại trên mỗi phần bổ sung.
Danh sách là ví dụ tiêu chuẩn.```python
xs = []
for i in range(100):
xs.append(i)
```Danh sách không phân bổ chính xác một vị trí mới cho mỗi phần bổ sung. Nó phát triển công suất theo các bước lớn hơn.
Về mặt khái niệm:```text
length = number of used entries
allocated = number of available slots
```Khi độ dài đạt đến dung lượng được phân bổ, CPython sẽ tăng mảng vật phẩm.
Điều này mang lại hành vi bổ sung hiệu quả được khấu hao.
Đánh đổi:```text
fewer reallocations
some unused spare capacity
```## 11.21 Thu nhỏ vùng chứa
Các vùng chứa có thể không trả lại bộ nhớ ngay lập tức khi chúng co lại.
Ví dụ:```python
xs = list(range(1_000_000))
del xs[:900_000]
```Độ dài logic giảm. Năng lực bên trong chỉ có thể co lại trong những điều kiện nhất định.
Điều này tránh được việc tái phân bổ tốn kém khi danh sách thu hẹp lại và phát triển liên tục.
Đối với mã nhạy cảm với bộ nhớ, việc tạo một vùng chứa nhỏ gọn mới đôi khi có thể giúp:```python
xs = xs[:]
```hoặc:```python
xs = list(xs)
```Nhưng hãy đo trước. Việc sao chép có thể tốn kém.
## 11.22 Chế độ xem bộ nhớ và bộ đệm
Một số đối tượng hiển thị bộ nhớ thông qua giao thức bộ đệm.
Ví dụ:```text
bytes
bytearray
array.array
memoryview
mmap objects
NumPy arrays
some extension objects
```Trình xuất bộ đệm có thể hiển thị bộ nhớ thô cho một đối tượng khác. Điều đó tạo ra những hạn chế suốt đời.
Ví dụ:```python
b = bytearray(b"hello")
v = memoryview(b)
```Trong khi chế độ xem bộ nhớ tồn tại, việc thay đổi kích thước bytearray cơ bản có thể bị hạn chế.
Ở cấp độ C, nhà xuất bộ đệm phải đảm bảo bộ nhớ vẫn hợp lệ trong khi người tiêu dùng giữ chế độ xem bộ đệm.
Điều này liên quan đến bộ cấp phát vì bộ nhớ không thể được giải phóng hoặc di chuyển trong khi chế độ xem bên ngoài phụ thuộc vào nó.
## 11.23 Hậu quả của việc phân bổ không di chuyển
Con trỏ đối tượng của CPython ổn định. Các đối tượng thường không được di chuyển bởi bộ thu gom rác nén.
Hậu quả:```text
PyObject * pointers remain valid while references are owned
C extensions can store object pointers
id(obj) can be address-like in CPython
memory cannot be compacted by moving live objects
fragmentation can accumulate
```Thiết kế không chuyển động là trọng tâm của khả năng tương thích API C.
Nó cũng giải thích tại sao CPython sử dụng đấu trường, nhóm, danh sách miễn phí và phân lớp phân bổ cẩn thận thay vì nén một đống.
## 11.24 Tùy chỉnh bộ phân bổ
CPython cho phép các trình nhúng và môi trường chuyên dụng tùy chỉnh bộ cấp phát.
Điều này hữu ích cho:```text
embedding Python in another application
sandboxing
memory accounting
debugging
custom allocation strategies
instrumentation
constrained runtimes
```Việc tùy chỉnh bộ cấp phát phải diễn ra cẩn thận và thường sớm trong quá trình khởi tạo thời gian chạy.
Bộ cấp phát thay thế phải tuân theo kỳ vọng của CPython đối với từng miền của bộ cấp phát.
Móc cấp phát xấu có thể làm hỏng trình thông dịch.
## 11.25 Tính toán bộ nhớ khó
Việc hiểu cách sử dụng bộ nhớ Python rất khó vì có nhiều lớp tương tác với nhau.
Một đối tượng Python có thể liên quan đến:```text
object header
object payload
auxiliary arrays
referenced objects
allocator padding
pool overhead
arena overhead
free-list retention
system allocator metadata
native library allocations
```Ví dụ:```python
xs = ["abc" for _ in range(1000)]
```Bộ nhớ bao gồm:```text
list object
list item array
1000 references in the array
string objects
string character data
allocator overhead
possibly interned or reused objects
sys.getsizeof(xs)chỉ báo cáo kích thước của đối tượng danh sách và bộ nhớ ngay lập tức của nó chứ không phải biểu đồ bắc cầu đầy đủ.
11.26sys.getsizeof
sys.getsizeoftrả về kích thước của một đối tượng như được đối tượng đó báo cáo.```python
import sys
xs = [1, 2, 3] print(sys.getsizeof(xs))
Ví dụ:```python
import sys
xs = [[1], [2], [3]]
print(sys.getsizeof(xs))
```Điều này bao gồm lưu trữ của danh sách bên ngoài, không phải danh sách bên trong và nội dung của chúng.
Hàm kích thước đệ quy phải duyệt qua các tham chiếu một cách cẩn thận và tránh tính hai lần các đối tượng dùng chung.
## 11.27 Các mẫu phân bổ phổ biến
Các mẫu phân bổ CPython phổ biến bao gồm:
| Mẫu | Ví dụ | Hành vi phân bổ |
| ------------------- | -------------- | -------------------------------------------- |
| Nhiều bộ dữ liệu nhỏ | trình phân tích cú pháp, công việc AST, vòng lặp | vấn đề cấp phát đối tượng nhỏ và danh sách miễn phí |
| Đối tượng byte lớn | I/O, tuần tự hóa | có thể bỏ qua bộ cấp phát đối tượng nhỏ |
| Danh sách ngày càng tăng | mã nặng nối thêm | vấn đề phân bổ quá mức |
| Những câu nói lớn | lập chỉ mục, JSON, toàn cầu | vấn đề tăng trưởng bảng băm |
| Khung | gọi hàm | vấn đề phân bổ và tái sử dụng khung |
| Ngoại lệ | đường dẫn nhiều lỗi | vấn đề truy nguyên và lưu giữ khung |
| Dây | định danh, phân tích cú pháp | Bố cục Unicode và vấn đề thực tập |
Công việc biểu diễn thường bắt đầu bằng việc tìm ra khuôn mẫu nào chiếm ưu thế.
## 11.28 Quy tắc phân bổ tiện ích mở rộng C
Các quy tắc thực tế dành cho tác giả phần mở rộng:
| Tình huống | Quy tắc |
| ----------------------------------- | -------------------------------------- |
| Phân bổ phiên bản đối tượng Python | Sử dụng máy phân bổ loại |
| Giải phóng phiên bản đối tượng Python | Sử dụng`tp_free`từ người giải quyết |
| Phân bổ bộ nhớ thời gian chạy phụ trợ | Sử dụng`PyMem_*`hoặc gia đình có giấy tờ |
| Phân bổ bộ nhớ đối tượng theo cách thủ công | Sử dụng`PyObject_*`chỉ khi thích hợp |
| Trả về đối tượng Python | Trả lại tài liệu tham khảo thuộc sở hữu |
| Lưu trữ trường đối tượng Python | Sở hữu một tài liệu tham khảo |
| Tái phân bổ bộ nhớ | Giữ con trỏ cũ cho đến khi thành công |
| Xử lý lỗi phân bổ | Đặt hoặc truyền bá`MemoryError`|
| Trộn phân bổ | Đừng làm điều đó |
Lỗi cấp phát thường nghiêm trọng. Chúng có thể xuất hiện dưới dạng sự cố khác xa với lỗi thực tế.
## 11.29 Mô hình tinh thần
Sử dụng mô hình này:```text
Python object allocation
type object chooses allocation path
object memory contains common header
object-specific fields are initialized
references are owned explicitly
deallocator releases references
tp_free releases memory
Small-object allocation
arenas contain pools
pools contain fixed-size blocks
small requests are served quickly
memory may be retained for reuse
```Quản lý bộ nhớ trong CPython là một hệ thống phân lớp:```text
reference counting decides when an object dies
cyclic GC finds unreachable cycles
deallocator releases object-owned resources
allocator reuses or frees memory blocks
system allocator manages process heap pages
operating system manages virtual memory
```Mỗi lớp trả lời một câu hỏi khác nhau.
##11.30 Tóm tắt
Hệ thống cấp phát bộ nhớ của CPython hỗ trợ phân bổ nhanh cho khối lượng công việc nặng về đối tượng của các chương trình Python. Các đồ vật nhỏ thường được phục vụ thông qua`pymalloc`, tổ chức bộ nhớ thành các đấu trường, nhóm và khối. Danh sách miễn phí theo loại cụ thể và tái sử dụng đối tượng tiếp tục giảm chi phí phân bổ cho các đối tượng phổ biến.
Đối với các tác giả tiện ích mở rộng C, các quy tắc quan trọng là kỷ luật nhóm cấp phát, xử lý chính xác lỗi phân bổ, phân bổ lại an toàn và phân tách rõ ràng giữa quyền sở hữu bộ nhớ và quyền sở hữu tham chiếu.
Bộ nhớ được giải phóng ở cấp đối tượng Python có thể được dự trữ bên trong CPython hoặc bộ cấp phát nền tảng. RSS cao sau khi xóa đối tượng không tự động có nghĩa là bị rò rỉ. CPython thường giữ sẵn bộ nhớ để sử dụng lại.