Quản lý bộ nhớ¶
Tổng quan¶
Quản lý bộ nhớ trong Python bao gồm một vùng riêng chứa tất cả các đối tượng và cấu trúc dữ liệu Python. Việc quản lý vùng nhớ riêng tư này được đảm bảo nội bộ bởi Python memory manager. Trình quản lý bộ nhớ Python có các thành phần khác nhau xử lý các khía cạnh quản lý lưu trữ động khác nhau, như chia sẻ, phân đoạn, phân bổ trước hoặc bộ nhớ đệm.
Ở mức thấp nhất, bộ cấp phát bộ nhớ thô đảm bảo có đủ chỗ trong vùng riêng để lưu trữ tất cả dữ liệu liên quan đến Python bằng cách tương tác với trình quản lý bộ nhớ của hệ điều hành. Ngoài bộ cấp phát bộ nhớ thô, một số bộ cấp phát dành riêng cho đối tượng hoạt động trên cùng một vùng nhớ và thực hiện các chính sách quản lý bộ nhớ riêng biệt phù hợp với đặc thù của từng loại đối tượng. Ví dụ: các đối tượng số nguyên được quản lý khác nhau trong vùng heap so với các chuỗi, bộ dữ liệu hoặc từ điển vì số nguyên ngụ ý các yêu cầu lưu trữ khác nhau và sự cân bằng giữa tốc độ/không gian. Do đó, trình quản lý bộ nhớ Python ủy quyền một số công việc cho các bộ cấp phát dành riêng cho đối tượng, nhưng đảm bảo rằng bộ cấp phát sau hoạt động trong giới hạn của vùng nhớ riêng.
Điều quan trọng là phải hiểu rằng việc quản lý vùng heap Python được thực hiện bởi chính trình thông dịch và người dùng không có quyền kiểm soát nó, ngay cả khi họ thường xuyên thao tác các con trỏ đối tượng tới các khối bộ nhớ bên trong vùng nhớ heap đó. Việc phân bổ vùng nhớ heap cho các đối tượng Python và các bộ đệm bên trong khác được trình quản lý bộ nhớ Python thực hiện theo yêu cầu thông qua các hàm Python/C API được liệt kê trong tài liệu này.
Để tránh hỏng bộ nhớ, người viết tiện ích mở rộng không bao giờ nên thử thao tác trên các đối tượng Python có các hàm được xuất bởi thư viện C: malloc(), calloc(), realloc() và free(). Điều này sẽ dẫn đến các cuộc gọi hỗn hợp giữa bộ cấp phát C và trình quản lý bộ nhớ Python với những hậu quả nghiêm trọng vì chúng triển khai các thuật toán khác nhau và hoạt động trên các vùng nhớ khác nhau. Tuy nhiên, người ta có thể phân bổ và giải phóng các khối bộ nhớ một cách an toàn bằng bộ cấp phát thư viện C cho các mục đích riêng lẻ, như trong ví dụ sau:
PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* cho I/O */
nếu (buf == NULL)
trả về PyErr_NoMemory();
...Thực hiện một số thao tác I/O liên quan đến buf...
res = PyBytes_FromString(buf);
miễn phí(buf); /* bị hỏng */
trả lại độ phân giải;
Trong ví dụ này, yêu cầu bộ nhớ cho bộ đệm I/O được bộ cấp phát thư viện C xử lý. Kết quả là trình quản lý bộ nhớ Python chỉ liên quan đến việc phân bổ đối tượng byte được trả về.
Tuy nhiên, trong hầu hết các trường hợp, bạn nên phân bổ bộ nhớ từ vùng heap Python một cách cụ thể vì vùng sau nằm dưới sự kiểm soát của trình quản lý bộ nhớ Python. Ví dụ: điều này là bắt buộc khi trình thông dịch được mở rộng với các kiểu đối tượng mới được viết bằng C. Một lý do khác để sử dụng vùng heap Python là mong muốn thông báo cho trình quản lý bộ nhớ Python về nhu cầu bộ nhớ của mô-đun mở rộng. Ngay cả khi bộ nhớ được yêu cầu chỉ được sử dụng cho các mục đích nội bộ, có tính đặc thù cao, việc ủy quyền tất cả các yêu cầu bộ nhớ cho trình quản lý bộ nhớ Python sẽ khiến trình thông dịch có được hình ảnh chính xác hơn về toàn bộ dấu chân bộ nhớ của nó. Do đó, trong một số trường hợp nhất định, trình quản lý bộ nhớ Python có thể kích hoạt hoặc không kích hoạt các hành động thích hợp, như thu gom rác, nén bộ nhớ hoặc các quy trình phòng ngừa khác. Lưu ý rằng bằng cách sử dụng bộ cấp phát thư viện C như trong ví dụ trước, bộ nhớ được cấp phát cho bộ đệm I/O sẽ thoát hoàn toàn khỏi trình quản lý bộ nhớ Python.
Xem thêm
Biến môi trường PYTHONMALLOC có thể được sử dụng để định cấu hình bộ cấp phát bộ nhớ được Python sử dụng.
Biến môi trường PYTHONMALLOCSTATS có thể được sử dụng để in số liệu thống kê của pymalloc memory allocator mỗi khi một trường đối tượng pymalloc mới được tạo và khi tắt máy.
Miền cấp phát¶
Tất cả các chức năng phân bổ thuộc về một trong ba "miền" khác nhau (xem thêm PyMemAllocatorDomain). Các miền này thể hiện các chiến lược phân bổ khác nhau và được tối ưu hóa cho các mục đích khác nhau. Chi tiết cụ thể về cách mỗi miền phân bổ bộ nhớ hoặc chức năng nội bộ mà mỗi miền gọi được coi là chi tiết triển khai, nhưng vì mục đích gỡ lỗi, bạn có thể tìm thấy bảng đơn giản hóa tại Bộ cấp phát bộ nhớ mặc định. Các API được sử dụng để phân bổ và giải phóng một khối bộ nhớ phải thuộc cùng một miền. Ví dụ: PyMem_Free() phải được sử dụng để giải phóng bộ nhớ được phân bổ bằng PyMem_Malloc().
Ba miền phân bổ là:
Miền thô: dành cho việc phân bổ bộ nhớ cho các bộ đệm bộ nhớ có mục đích chung trong đó việc phân bổ must được chuyển đến bộ cấp phát hệ thống hoặc nơi bộ cấp phát có thể hoạt động mà không cần attached thread state. Bộ nhớ được yêu cầu trực tiếp từ hệ thống. Xem Raw Memory Interface.
Miền "Mem": dành cho việc phân bổ bộ nhớ cho bộ đệm Python và bộ đệm bộ nhớ đa năng trong đó việc phân bổ phải được thực hiện bằng attached thread state. Bộ nhớ được lấy từ vùng riêng của Python. Xem Memory Interface.
Miền đối tượng: dùng để phân bổ bộ nhớ cho các đối tượng Python. Bộ nhớ được lấy từ vùng riêng của Python. Xem Object allocators.
Ghi chú
Bản dựng free-threaded yêu cầu chỉ các đối tượng Python được phân bổ bằng miền "đối tượng" và tất cả các đối tượng Python được phân bổ bằng miền đó. Điều này khác với các phiên bản Python trước đó, đây chỉ là cách thực hành tốt nhất chứ không phải là một yêu cầu khó.
Ví dụ: bộ đệm (đối tượng không phải Python) phải được phân bổ bằng PyMem_Malloc(), PyMem_RawMalloc() hoặc malloc() chứ không phải PyObject_Malloc().
Giao diện bộ nhớ thô¶
Các bộ chức năng sau đây là các hàm bao cho bộ cấp phát hệ thống. Các chức năng này an toàn theo luồng, vì vậy thread state không cần phải là attached.
default raw memory allocator sử dụng các chức năng sau: malloc(), calloc(), realloc() và free(); gọi malloc(1) (hoặc calloc(1, 1)) khi yêu cầu byte 0.
Added in version 3.4.
-
void *PyMem_RawMalloc(size_t n)¶
- Một phần của ABI ổn định kể từ phiên bản 3.13.
Phân bổ byte n và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công.Yêu cầu byte 0 sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyMem_RawMalloc(1)đã được gọi thay thế. Bộ nhớ sẽ không được khởi tạo theo bất kỳ cách nào.
-
void *PyMem_RawCalloc(size_t nelem, size_t elsize)¶
- Một phần của ABI ổn định kể từ phiên bản 3.13.
Phân bổ các phần tử nelem, mỗi phần tử có kích thước tính bằng byte là elsize và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công. Bộ nhớ được khởi tạo về số không.Yêu cầu các phần tử bằng 0 hoặc các phần tử có kích thước 0 byte sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyMem_RawCalloc(1, 1)đã được gọi thay thế.Added in version 3.5.
-
void *PyMem_RawRealloc(void *p, size_t n)¶
- Một phần của ABI ổn định kể từ phiên bản 3.13.
Thay đổi kích thước khối bộ nhớ được trỏ bởi p thành n byte. Nội dung sẽ không thay đổi ở mức tối thiểu của kích thước cũ và mới.
Nếu p là
NULL, lệnh gọi tương đương vớiPyMem_RawMalloc(n); ngược lại, nếu n bằng 0, khối bộ nhớ sẽ được thay đổi kích thước nhưng không được giải phóng và con trỏ trả về không phải làNULL.Trừ khi p là
NULL, nó phải được trả về bằng lệnh gọi trước đó tớiPyMem_RawMalloc(),PyMem_RawRealloc()hoặcPyMem_RawCalloc().Nếu yêu cầu không thành công,
PyMem_RawRealloc()trả vềNULLvà p vẫn là một con trỏ hợp lệ tới vùng bộ nhớ trước đó.
-
void PyMem_RawFree(void *p)¶
- Một phần của ABI ổn định kể từ phiên bản 3.13.
Giải phóng khối bộ nhớ được trỏ tới bởi p, khối này phải được trả về bằng lệnh gọi trước đó tới
PyMem_RawMalloc(),PyMem_RawRealloc()hoặcPyMem_RawCalloc(). Ngược lại, hoặc nếuPyMem_RawFree(p)đã được gọi trước đó, hành vi không xác định sẽ xảy ra.Nếu p là
NULLthì không có thao tác nào được thực hiện.
Giao diện bộ nhớ¶
Các bộ hàm sau, được mô hình hóa theo tiêu chuẩn ANSI C, nhưng chỉ định hành vi khi yêu cầu byte 0, có sẵn để phân bổ và giải phóng bộ nhớ khỏi vùng heap Python.
Trong bản dựng hỗ trợ GIL (bản dựng mặc định), default memory allocator sử dụng pymalloc memory allocator, trong khi ở free-threaded build, mặc định là mimalloc memory allocator.
Cảnh báo
Phải có attached thread state khi sử dụng các chức năng này.
Thay đổi trong phiên bản 3.6: Bộ cấp phát mặc định bây giờ là pymalloc thay vì hệ thống malloc().
Thay đổi trong phiên bản 3.13: Trong bản dựng free-threaded, bộ cấp phát mặc định hiện là mimalloc.
-
void *PyMem_Malloc(size_t n)¶
- Một phần của ABI ổn định.
Phân bổ byte n và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công.Yêu cầu byte 0 sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyMem_Malloc(1)đã được gọi thay thế. Bộ nhớ sẽ không được khởi tạo theo bất kỳ cách nào.
-
void *PyMem_Calloc(size_t nelem, size_t elsize)¶
- Một phần của ABI ổn định kể từ phiên bản 3.7.
Phân bổ các phần tử nelem, mỗi phần tử có kích thước tính bằng byte là elsize và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công. Bộ nhớ được khởi tạo về số không.Yêu cầu các phần tử bằng 0 hoặc các phần tử có kích thước bằng 0 byte sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyMem_Calloc(1, 1)đã được gọi thay thế.Added in version 3.5.
-
void *PyMem_Realloc(void *p, size_t n)¶
- Một phần của ABI ổn định.
Thay đổi kích thước khối bộ nhớ được trỏ bởi p thành n byte. Nội dung sẽ không thay đổi ở mức tối thiểu của kích thước cũ và mới.
Nếu p là
NULL, lệnh gọi tương đương vớiPyMem_Malloc(n); ngược lại, nếu n bằng 0, khối bộ nhớ sẽ được thay đổi kích thước nhưng không được giải phóng và con trỏ trả về không phải làNULL.Trừ khi p là
NULL, nó phải được trả về bằng lệnh gọi trước đó tớiPyMem_Malloc(),PyMem_Realloc()hoặcPyMem_Calloc().Nếu yêu cầu không thành công,
PyMem_Realloc()trả vềNULLvà p vẫn là một con trỏ hợp lệ tới vùng bộ nhớ trước đó.
-
void PyMem_Free(void *p)¶
- Một phần của ABI ổn định.
Giải phóng khối bộ nhớ được trỏ tới bởi p, khối này phải được trả về bằng lệnh gọi trước đó tới
PyMem_Malloc(),PyMem_Realloc()hoặcPyMem_Calloc(). Ngược lại, hoặc nếuPyMem_Free(p)đã được gọi trước đó, hành vi không xác định sẽ xảy ra.Nếu p là
NULLthì không có thao tác nào được thực hiện.
Các macro định hướng kiểu sau đây được cung cấp để thuận tiện. Lưu ý rằng TYPE đề cập đến bất kỳ loại C nào.
-
PyMem_New(TYPE, n)¶
Tương tự như
PyMem_Malloc(), nhưng phân bổ byte bộ nhớ(n * sizeof(TYPE)). Trả về một con trỏ được truyền tớiTYPE*. Bộ nhớ sẽ không được khởi tạo theo bất kỳ cách nào.
-
PyMem_Resize(p, TYPE, n)¶
Tương tự như
PyMem_Realloc(), nhưng khối bộ nhớ được thay đổi kích thước thành byte(n * sizeof(TYPE)). Trả về một con trỏ được truyền tớiTYPE*. Khi quay lại, p sẽ là con trỏ tới vùng bộ nhớ mới hoặcNULLtrong trường hợp bị lỗi.Đây là macro tiền xử lý C; p luôn được chỉ định lại. Lưu lại giá trị ban đầu của p để tránh bị mất bộ nhớ khi xử lý lỗi.
-
void PyMem_Del(void *p)¶
Tương tự với
PyMem_Free()
Bí danh không được dùng nữa¶
Đây là các bí danh soft deprecated cho các hàm và macro hiện có. Chúng tồn tại chỉ để tương thích ngược.
Bí danh không được dùng nữa |
Chức năng hoặc macro tương ứng |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
Thay đổi trong phiên bản 3.4: Các macro bây giờ là bí danh của các hàm và macro tương ứng. Trước đây, hành vi của chúng giống nhau, nhưng việc sử dụng chúng không nhất thiết đảm bảo khả năng tương thích nhị phân giữa các phiên bản Python.
Sắp loại bỏ từ phiên bản 2.0.
Bộ cấp phát đối tượng¶
Các bộ hàm sau, được mô hình hóa theo tiêu chuẩn ANSI C, nhưng chỉ định hành vi khi yêu cầu byte 0, có sẵn để phân bổ và giải phóng bộ nhớ khỏi vùng heap Python.
Ghi chú
Không có gì đảm bảo rằng bộ nhớ được trả về bởi các bộ cấp phát này có thể được chuyển thành công sang đối tượng Python khi chặn các hàm cấp phát trong miền này bằng các phương thức được mô tả trong phần Customize Memory Allocators.
Zz000zz sử dụng pymalloc memory allocator. Trong bản dựng free-threaded, mặc định là mimalloc memory allocator.
Cảnh báo
Phải có attached thread state khi sử dụng các chức năng này.
-
void *PyObject_Malloc(size_t n)¶
- Một phần của ABI ổn định.
Phân bổ byte n và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công.Yêu cầu byte 0 sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyObject_Malloc(1)đã được gọi thay thế. Bộ nhớ sẽ không được khởi tạo theo bất kỳ cách nào.
-
void *PyObject_Calloc(size_t nelem, size_t elsize)¶
- Một phần của ABI ổn định kể từ phiên bản 3.7.
Phân bổ các phần tử nelem, mỗi phần tử có kích thước tính bằng byte là elsize và trả về một con trỏ loại void* cho bộ nhớ được phân bổ hoặc
NULLnếu yêu cầu không thành công. Bộ nhớ được khởi tạo về số không.Yêu cầu các phần tử bằng 0 hoặc các phần tử có kích thước 0 byte sẽ trả về một con trỏ không phải
NULLriêng biệt nếu có thể, như thểPyObject_Calloc(1, 1)đã được gọi thay thế.Added in version 3.5.
-
void *PyObject_Realloc(void *p, size_t n)¶
- Một phần của ABI ổn định.
Thay đổi kích thước khối bộ nhớ được trỏ bởi p thành n byte. Nội dung sẽ không thay đổi ở mức tối thiểu của kích thước cũ và mới.
Nếu p là
NULL, lệnh gọi tương đương vớiPyObject_Malloc(n); ngược lại, nếu n bằng 0, khối bộ nhớ sẽ được thay đổi kích thước nhưng không được giải phóng và con trỏ trả về không phải làNULL.Trừ khi p là
NULL, nó phải được trả về bằng lệnh gọi trước đó tớiPyObject_Malloc(),PyObject_Realloc()hoặcPyObject_Calloc().Nếu yêu cầu không thành công,
PyObject_Realloc()trả vềNULLvà p vẫn là một con trỏ hợp lệ tới vùng bộ nhớ trước đó.
-
void PyObject_Free(void *p)¶
- Một phần của ABI ổn định.
Giải phóng khối bộ nhớ được trỏ tới bởi p, khối này phải được trả về bằng lệnh gọi trước đó tới
PyObject_Malloc(),PyObject_Realloc()hoặcPyObject_Calloc(). Ngược lại, hoặc nếuPyObject_Free(p)đã được gọi trước đó, hành vi không xác định sẽ xảy ra.Nếu p là
NULLthì không có thao tác nào được thực hiện.Đừng gọi trực tiếp điều này để giải phóng bộ nhớ của đối tượng; thay vào đó hãy gọi loại
tp_freeslot.Không sử dụng điều này cho bộ nhớ được phân bổ bởi
PyObject_GC_NewhoặcPyObject_GC_NewVar; thay vào đó hãy sử dụngPyObject_GC_Del().Xem thêm
PyObject_GC_Del()tương đương với chức năng này dành cho bộ nhớ được phân bổ theo loại hỗ trợ thu gom rác.
Bộ cấp phát bộ nhớ mặc định¶
Cấp phát bộ nhớ mặc định:
Cấu hình |
Tên |
PyMem_RawMalloc |
PyMem_Malloc |
PyObject_Malloc |
|---|---|---|---|---|
Bản phát hành |
|
|
|
|
Bản dựng gỡ lỗi |
|
|
|
|
Phát hành bản dựng, không có pymalloc |
|
|
|
|
Bản dựng gỡ lỗi, không có pymalloc |
|
|
|
|
Xây dựng theo luồng miễn phí |
|
|
|
|
Xây dựng gỡ lỗi theo luồng miễn phí |
|
|
|
|
Huyền thoại:
Tên: giá trị cho biến môi trường
PYTHONMALLOC.malloc: bộ cấp phát hệ thống từ thư viện C tiêu chuẩn, các hàm C:malloc(),calloc(),realloc()vàfree().pymalloc: pymalloc memory allocator.mimalloc: mimalloc memory allocator."+ gỡ lỗi": với debug hooks on the Python memory allocators.
"Bản dựng gỡ lỗi": Python build in debug mode.
Tùy chỉnh bộ cấp phát bộ nhớ¶
Added in version 3.4.
-
type PyMemAllocatorEx¶
Cấu trúc được sử dụng để mô tả bộ cấp phát khối bộ nhớ. Cấu trúc có các trường sau:
Cánh đồng
Nghĩa
void *ctxbối cảnh người dùng được chuyển làm đối số đầu tiên
void* malloc(void *ctx, size_t size)cấp phát khối bộ nhớ
void* calloc(void *ctx, size_t nelem, size_t elsize)phân bổ một khối bộ nhớ được khởi tạo bằng số 0
void* realloc(void *ctx, void *ptr, size_t new_size)phân bổ hoặc thay đổi kích thước khối bộ nhớ
void free(void *ctx, void *ptr)giải phóng một khối bộ nhớ
Thay đổi trong phiên bản 3.5: Cấu trúc
PyMemAllocatorđược đổi tên thànhPyMemAllocatorExvà trườngcallocmới đã được thêm vào.
-
type PyMemAllocatorDomain¶
Enum được sử dụng để xác định miền cấp phát. Tên miền:
-
PYMEM_DOMAIN_RAW¶
Chức năng:
-
PYMEM_DOMAIN_MEM¶
Chức năng:
-
PYMEM_DOMAIN_OBJ¶
Chức năng:
-
PYMEM_DOMAIN_RAW¶
-
void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
Lấy bộ cấp phát khối bộ nhớ của miền được chỉ định.
-
void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
Đặt bộ cấp phát khối bộ nhớ của miền được chỉ định.
Bộ cấp phát mới phải trả về một con trỏ không phải
NULLriêng biệt khi yêu cầu byte 0.Đối với miền
PYMEM_DOMAIN_RAW, bộ cấp phát phải an toàn theo luồng: thread state không phải là attached khi bộ cấp phát được gọi.Đối với các miền còn lại, bộ cấp phát cũng phải an toàn theo luồng: bộ cấp phát có thể được gọi trong các trình thông dịch khác nhau không dùng chung GIL.
Nếu bộ cấp phát mới không phải là một hook (không gọi bộ cấp phát trước đó), hàm
PyMem_SetupDebugHooks()phải được gọi để cài đặt lại các móc gỡ lỗi ở trên cùng của bộ cấp phát mới.Xem thêm
PyPreConfig.allocatorvà Preinitialize Python with PyPreConfig.Cảnh báo
PyMem_SetAllocator()có hợp đồng sau:Nó có thể được gọi sau
Py_PreInitialize()và trướcPy_InitializeFromConfig()để cài đặt bộ cấp phát bộ nhớ tùy chỉnh. Không có hạn chế nào đối với bộ cấp phát đã cài đặt ngoài những hạn chế do miền áp đặt (ví dụ: Miền thô cho phép gọi bộ cấp phát mà không cần attached thread state). Xem the section on allocator domains để biết thêm thông tin.Nếu được gọi sau khi Python khởi tạo xong (sau khi
Py_InitializeFromConfig()được gọi), bộ cấp phát must sẽ bao bọc bộ cấp phát hiện có. Thay thế bộ cấp phát hiện tại bằng một số tùy ý khác là not supported.
Thay đổi trong phiên bản 3.12: Tất cả các bộ cấp phát phải an toàn theo luồng.
-
void PyMem_SetupDebugHooks(void)¶
Thiết lập debug hooks in the Python memory allocators để phát hiện lỗi bộ nhớ.
Móc gỡ lỗi trên bộ cấp phát bộ nhớ Python¶
Khi Python is built in debug mode, hàm PyMem_SetupDebugHooks() được gọi tại Python preinitialization để thiết lập móc gỡ lỗi trên bộ cấp phát bộ nhớ Python nhằm phát hiện lỗi bộ nhớ.
Biến môi trường PYTHONMALLOC có thể được sử dụng để cài đặt móc gỡ lỗi trên Python được biên dịch ở chế độ phát hành (ví dụ: PYTHONMALLOC=debug).
Hàm PyMem_SetupDebugHooks() có thể được sử dụng để đặt móc gỡ lỗi sau khi gọi PyMem_SetAllocator().
Các móc gỡ lỗi này lấp đầy các khối bộ nhớ được phân bổ động bằng các mẫu bit đặc biệt, dễ nhận biết. Bộ nhớ được cấp phát mới chứa byte 0xCD (PYMEM_CLEANBYTE), bộ nhớ giải phóng chứa byte 0xDD (PYMEM_DEADBYTE). Các khối bộ nhớ được bao quanh bởi các "byte bị cấm" chứa đầy byte 0xFD (PYMEM_FORBIDDENBYTE). Các chuỗi byte này không chắc là địa chỉ hợp lệ, số float hoặc chuỗi ASCII.
Kiểm tra thời gian chạy:
Phát hiện vi phạm API. Ví dụ: phát hiện xem
PyObject_Free()có được gọi trên khối bộ nhớ được phân bổ bởiPyMem_Malloc()hay không.Phát hiện ghi trước khi bắt đầu bộ đệm (tràn bộ đệm).
Phát hiện ghi sau khi kết thúc bộ đệm (tràn bộ đệm).
Kiểm tra xem có attached thread state hay không khi các hàm cấp phát của miền
PYMEM_DOMAIN_OBJ(ví dụ:PyObject_Malloc()) vàPYMEM_DOMAIN_MEM(ví dụ:PyMem_Malloc()) được gọi.
Nếu có lỗi, móc gỡ lỗi sử dụng mô-đun tracemalloc để lấy lại dấu vết nơi khối bộ nhớ được phân bổ. Truy nguyên chỉ được hiển thị nếu tracemalloc đang theo dõi việc phân bổ bộ nhớ Python và khối bộ nhớ đã được theo dõi.
Đặt S = sizeof(size_t). Các byte 2*S được thêm vào mỗi đầu của mỗi khối byte N được yêu cầu. Bố cục bộ nhớ là như vậy, trong đó p đại diện cho địa chỉ được trả về bởi hàm giống malloc hoặc giống realloc (p[i:j] có nghĩa là lát byte từ *(p+i) bao gồm tối đa *(p+j) độc quyền; lưu ý rằng việc xử lý các chỉ số âm khác với một lát Python):
p[-2*S:-S]Số byte được yêu cầu ban đầu. Đây là size_t, big endian (dễ đọc hơn trong kết xuất bộ nhớ).
p[-S]mã định danh API (ký tự ASCII):
'r'choPYMEM_DOMAIN_RAW.'m'choPYMEM_DOMAIN_MEM.'o'choPYMEM_DOMAIN_OBJ.
p[-S+1:0]Bản sao của PYMEM_FORBIDDENBYTE. Được sử dụng để bắt lỗi ghi và đọc.
p[0:N]Bộ nhớ được yêu cầu chứa đầy các bản sao của PYMEM_CLEANBYTE, được sử dụng để bắt tham chiếu đến bộ nhớ chưa được khởi tạo. Khi một hàm giống realloc được gọi yêu cầu khối bộ nhớ lớn hơn, các byte dư thừa mới cũng được lấp đầy bằng PYMEM_CLEANBYTE. Khi một hàm giống tự do được gọi, chúng sẽ được ghi đè bằng PYMEM_DEADBYTE để bắt tham chiếu đến bộ nhớ đã giải phóng. Khi một hàm giống realloc được gọi yêu cầu khối bộ nhớ nhỏ hơn, các byte cũ dư thừa cũng được lấp đầy bằng PYMEM_DEADBYTE.
p[N:N+S]Bản sao của PYMEM_FORBIDDENBYTE. Được sử dụng để bắt ghi đè và đọc.
p[N+S:N+2*S]Chỉ được sử dụng nếu macro
PYMEM_DEBUG_SERIALNOđược xác định (không được xác định theo mặc định).Một số sê-ri, tăng thêm 1 trên mỗi lệnh gọi tới hàm giống malloc hoặc giống realloc. Zz000zz lớn. Nếu "bộ nhớ kém" được phát hiện sau đó, số sê-ri sẽ là một cách tuyệt vời để đặt điểm dừng trong lần chạy tiếp theo, để ghi lại thời điểm khối này được chuyển đi. Hàm tĩnhbumserialno() trong obmalloc.c là nơi duy nhất số sê-ri được tăng lên và tồn tại để bạn có thể dễ dàng đặt điểm dừng như vậy.
Trước tiên, một hàm giống realloc hoặc giống tự do sẽ kiểm tra xem các byte PYMEM_FORBIDDENBYTE ở mỗi đầu có còn nguyên vẹn hay không. Nếu chúng đã bị thay đổi, đầu ra chẩn đoán sẽ được ghi vào stderr và chương trình sẽ bị hủy qua Py_FatalError(). Kiểu lỗi chính khác là gây ra lỗi bộ nhớ khi một chương trình đọc một trong các mẫu bit đặc biệt và cố gắng sử dụng nó làm địa chỉ. Nếu bạn vào trình gỡ lỗi và nhìn vào đối tượng, bạn có thể thấy rằng nó hoàn toàn chứa đầy PYMEM_DEADBYTE (có nghĩa là bộ nhớ giải phóng đang được sử dụng) hoặc PYMEM_CLEANBYTE (có nghĩa là bộ nhớ chưa được khởi tạo đang được sử dụng).
Thay đổi trong phiên bản 3.6: Hàm PyMem_SetupDebugHooks() hiện cũng hoạt động trên Python được biên dịch ở chế độ phát hành. Do lỗi, các móc gỡ lỗi hiện sử dụng tracemalloc để lấy lại dấu vết nơi khối bộ nhớ được phân bổ. Các móc gỡ lỗi hiện cũng kiểm tra xem có attached thread state hay không khi các hàm của miền PYMEM_DOMAIN_OBJ và PYMEM_DOMAIN_MEM được gọi.
Thay đổi trong phiên bản 3.8: Các mẫu byte 0xCB (PYMEM_CLEANBYTE), 0xDB (PYMEM_DEADBYTE) và 0xFB (PYMEM_FORBIDDENBYTE) đã được thay thế bằng 0xCD, 0xDD và 0xFD để sử dụng cùng các giá trị so với Windows CRT debug malloc() và free().
Bộ cấp phát pymalloc¶
Python có bộ cấp phát pymalloc được tối ưu hóa cho các đối tượng nhỏ (nhỏ hơn hoặc bằng 512 byte) với thời gian tồn tại ngắn. Nó sử dụng ánh xạ bộ nhớ được gọi là "đấu trường" với kích thước cố định là 256 KiB trên nền tảng 32 bit hoặc 1 MiB trên nền tảng 64 bit. Nó quay trở lại PyMem_RawMalloc() và PyMem_RawRealloc() đối với các phân bổ lớn hơn 512 byte.
pymalloc là default allocator của các miền PYMEM_DOMAIN_MEM (ví dụ: PyMem_Malloc()) và PYMEM_DOMAIN_OBJ (ví dụ: PyObject_Malloc()).
Bộ cấp phát đấu trường sử dụng các chức năng sau:
VirtualAlloc()vàVirtualFree()trên Windows,mmap()vàmunmap()nếu có,malloc()vàfree()nếu không.
Bộ cấp phát này bị vô hiệu hóa nếu Python được định cấu hình với tùy chọn --without-pymalloc. Nó cũng có thể bị vô hiệu hóa trong thời gian chạy bằng cách sử dụng biến môi trường PYTHONMALLOC (ví dụ: PYTHONMALLOC=malloc).
Thông thường, nên tắt bộ cấp phát pymalloc khi xây dựng Python bằng addressSanitizer (--with-address-sanitizer) để giúp phát hiện các lỗi cấp thấp trong mã C.
Tùy chỉnh Bộ phân bổ Arena pymalloc¶
Added in version 3.4.
-
type PyObjectArenaAllocator¶
Cấu trúc được sử dụng để mô tả một bộ cấp phát đấu trường. Cấu trúc có ba trường:
Cánh đồng
Nghĩa
void *ctxbối cảnh người dùng được chuyển làm đối số đầu tiên
void* alloc(void *ctx, size_t size)phân bổ một trường có kích thước byte
void free(void *ctx, void *ptr, size_t size)giải phóng một đấu trường
-
void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)¶
Nhận công cụ phân bổ đấu trường.
-
void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)¶
Đặt bộ phân bổ đấu trường.
Bộ cấp phát mimalloc¶
Added in version 3.13.
Python hỗ trợ bộ cấp phát mimalloc khi có hỗ trợ nền tảng cơ bản. mimalloc là một công cụ cấp phát có mục đích chung với các đặc tính hiệu suất tuyệt vời, ban đầu được Daan Leijen phát triển cho hệ thống thời gian chạy của ngôn ngữ Koka và Lean.
Không giống như pymalloc, được tối ưu hóa cho các đối tượng nhỏ (512 byte trở xuống), mimalloc xử lý việc phân bổ ở mọi kích thước.
Trong bản dựng free-threaded, mimalloc là bộ cấp phát mặc định và required cho các miền PYMEM_DOMAIN_MEM và PYMEM_DOMAIN_OBJ. Nó không thể bị vô hiệu hóa trong các bản dựng có luồng tự do. Bản dựng luồng tự do sử dụng các vùng nhớ mimalloc trên mỗi luồng, cho phép tiến hành phân bổ và giải phóng mà không cần khóa trong hầu hết các trường hợp.
Trong bản dựng mặc định (không có luồng tự do), mimalloc có sẵn nhưng không có bộ cấp phát mặc định. Nó có thể được chọn trong thời gian chạy bằng cách sử dụng PYTHONMALLOC=mimalloc (hoặc mimalloc_debug để bao gồm debug hooks). Nó có thể bị vô hiệu hóa tại thời điểm xây dựng bằng tùy chọn cấu hình --without-mimalloc, nhưng không thể kết hợp tùy chọn này với --disable-gil.
tracemalloc C API¶
Added in version 3.7.
-
int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)¶
Theo dõi khối bộ nhớ được phân bổ trong mô-đun
tracemalloc.Trả về
0nếu thành công, trả về-1nếu có lỗi (không phân bổ được bộ nhớ để lưu dấu vết). Trả về-2nếu tracemalloc bị tắt.Nếu khối bộ nhớ đã được theo dõi, hãy cập nhật dấu vết hiện có.
-
int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)¶
Bỏ theo dõi khối bộ nhớ được phân bổ trong mô-đun
tracemalloc. Không làm gì nếu khối không được theo dõi.Trả về
-2nếu tracemalloc bị tắt, nếu không thì trả về0.
Ví dụ¶
Đây là ví dụ từ phần Tổng quan, được viết lại để bộ đệm I/O được phân bổ từ vùng heap Python bằng cách sử dụng bộ hàm đầu tiên:
PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* cho I/O */
nếu (buf == NULL)
trả về PyErr_NoMemory();
/* ...Thực hiện một số thao tác I/O liên quan đến buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* được phân bổ bằng PyMem_Malloc */
trả lại độ phân giải;
Mã tương tự sử dụng bộ hàm định hướng loại
PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* cho I/O */
nếu (buf == NULL)
trả về PyErr_NoMemory();
/* ...Thực hiện một số thao tác I/O liên quan đến buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* được phân bổ bằng PyMem_New */
trả lại độ phân giải;
Lưu ý rằng trong hai ví dụ trên, bộ đệm luôn được thao tác thông qua các hàm thuộc cùng một tập hợp. Thật vậy, cần phải sử dụng cùng một họ bộ nhớ API cho một khối bộ nhớ nhất định để giảm thiểu nguy cơ trộn lẫn các bộ cấp phát khác nhau. Chuỗi mã sau đây chứa hai lỗi, một trong số đó được gắn nhãn là fatal vì nó trộn lẫn hai bộ cấp phát khác nhau hoạt động trên các vùng nhớ khác nhau.
char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3); /* Sai -- phải là PyMem_Free() */
miễn phí(buf2); /* Phải -- được phân bổ qua malloc() */
miễn phí(buf1); /* Gây tử vong -- phải là PyMem_Free() */
Ngoài các chức năng nhằm xử lý các khối bộ nhớ thô từ vùng heap Python, các đối tượng trong Python còn được phân bổ và giải phóng với PyObject_New, PyObject_NewVar và PyObject_Free().
Những điều này sẽ được giải thích trong chương tiếp theo về việc xác định và triển khai các loại đối tượng mới trong C.