10. Người thu gom rác
10. Người thu gom rác
CPython sử dụng tính năng tham chiếu làm cơ chế quản lý bộ nhớ chính. Việc đếm tham chiếu sẽ phá hủy hầu hết các đối tượng ngay khi tham chiếu mạnh cuối cùng của chúng biến mất.
Việc đếm tham chiếu có một hạn chế lớn: nó không thể tự lấy lại các chu kỳ tham chiếu.
Trình thu gom rác tồn tại để tìm các chu kỳ không thể truy cập được của các đối tượng vùng chứa và lấy lại chúng. Nó là phần bổ sung cho việc đếm tham chiếu chứ không phải là sự thay thế cho nó.
10.1 Tại sao cần trợ giúp về tính toán tham chiếu
Số lượng tham chiếu chỉ đạt 0 khi không có tham chiếu mạnh nào trỏ đến một đối tượng.
Điều này hoạt động cho các đồ thị đối tượng thông thường:```python x = [] del x
Nó không hoạt động theo chu kỳ:```python
a = []
b = []
a.append(b)
b.append(a)
del a
del b
```Sau khi hai tên bị xóa, các danh sách vẫn tham chiếu lẫn nhau:```text
list A ---> list B
list B ---> list A
```Số lượng tham chiếu của họ vẫn khác không. Nhưng không có mã Python trực tiếp nào có thể tiếp cận được chúng.
Việc đếm tham chiếu cho thấy quyền sở hữu cục bộ. Bộ sưu tập rác nhìn thấy khả năng tiếp cận.
## 10.2 Người thu gom rác theo dõi những gì
CPython không cần theo dõi mọi đối tượng trong trình thu gom rác tuần hoàn.
Các đối tượng không thể chứa các tham chiếu đến các đối tượng Python khác thì không thể tự hình thành các chu trình. Ví dụ bao gồm nhiều số nguyên, số float và chuỗi đơn giản.
Bộ sưu tập chủ yếu theo dõi các đối tượng giống như thùng chứa:```text
list
dict
set
tuple containing references
function
class
instance
frame
generator
coroutine
traceback
some extension objects
```Một đối tượng cần theo dõi GC khi nó có thể tham gia vào một chu trình.
Ví dụ:```python
x = 123
```Đối tượng số nguyên không trỏ đến các đối tượng Python khác theo cách tạo ra các chu trình vùng chứa.
Nhưng:```python
x = []
x.append(x)
```Danh sách trỏ đến chính nó. Đây là một chu kỳ.
## 10.3 Tự chu kỳ
Chu trình tham chiếu đơn giản nhất là chu trình tự chu trình:```python
x = []
x.append(x)
```Cấu trúc là:```text
x ---> list
^ |
| |
+--+
```Bây giờ xóa tên bên ngoài:```python
del x
```Danh sách vẫn chứa một tham chiếu đến chính nó. Số tham chiếu của nó vẫn ở trên 0.
Không có biến chương trình nào có thể tiếp cận được nó, nhưng chỉ tính tham chiếu thôi thì không thể phá hủy được nó. Trình thu gom rác tuần hoàn phải phát hiện ra nó.
## 10.4 Chu kỳ đa đối tượng
Chu kỳ thường liên quan đến nhiều đối tượng.```python
class Node:
def __init__(self):
self.parent = None
self.children = []
root = Node()
child = Node()
root.children.append(child)
child.parent = root
del root
del child
```Biểu đồ trở nên không thể truy cập được nhưng các tham chiếu vẫn còn bên trong biểu đồ:```text
root node ---> children list ---> child node
^ |
| |
+----------- parent -----------+
```Mẫu này phổ biến trong cây, đồ thị, mô hình đối tượng, AST, DOM, bộ đệm, khung, bao đóng và truy ngược ngoại lệ.
## 10.5 Collector hoạt động trên Container
Bộ thu thập tuần hoàn chỉ cần suy luận về các đối tượng có thể trỏ đến các đối tượng khác.
Nó hỏi từng đối tượng được theo dõi:```text
Which Python objects do you reference?
```Ở cấp độ C, các loại tiện ích mở rộng trả lời thông qua hỗ trợ truyền tải. Loại nhận biết GC cung cấp chức năng truyền tải truy cập các tham chiếu Python có chứa.
Về mặt khái niệm:```c
static int
Node_traverse(NodeObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
Py_VISIT(self->children);
return 0;
}
```Bộ sưu tập sử dụng điều này để đi qua đồ thị đối tượng.
Nếu một đối tượng mở rộng sở hữu các tham chiếu Python và có thể là một phần của chu trình thì nó phải tham gia vào giao thức này. Nếu không, nó có thể rò rỉ chu kỳ.
## 10.6 Đối tượng được theo dõi và không bị theo dõi
Một đối tượng có thể được theo dõi hoặc không bị theo dõi bởi bộ thu thập tuần hoàn.
Được theo dõi có nghĩa là người thu gom có thể kiểm tra nó trong quá trình thu thập.
Không bị theo dõi có nghĩa là bộ thu thập bỏ qua nó để phát hiện chu kỳ.
Bạn có thể kiểm tra điều này từ Python:```python
import gc
print(gc.is_tracked([]))
print(gc.is_tracked(123))
print(gc.is_tracked("hello"))
```Đầu ra điển hình trên CPython có thể cho thấy các thùng chứa được theo dõi còn các đối tượng nguyên tử thì không. Kết quả chính xác có thể khác nhau do CPython áp dụng tối ưu hóa. Ví dụ: một số vùng chứa có thể không bị theo dõi khi chúng chỉ chứa các đối tượng nguyên tử.
Quy tắc quan trọng mang tính khái niệm:```text
objects that can participate in cycles need tracking
objects that cannot participate in cycles usually do not
```## 10.7 Bộ sưu tập thế hệ
Trình thu gom rác theo chu kỳ của CPython mang tính thế hệ.
Ý tưởng này dựa trên một quan sát chung: hầu hết các vật thể đều chết trẻ. Các đối tượng tồn tại qua nhiều bộ sưu tập có khả năng tồn tại lâu hơn.
Một nhóm nhà sưu tập thế hệ đã theo dõi các đồ vật theo độ tuổi. Thế hệ trẻ được thu thập thường xuyên hơn. Các thế hệ cũ được thu thập ít thường xuyên hơn.
Về mặt khái niệm:```text
generation 0
newest tracked objects
collected most often
generation 1
objects that survived earlier collection
collected less often
generation 2
older tracked objects
collected least often
```Thiết kế thế hệ chính xác có thể thay đổi trên các phiên bản CPython. Mô hình tinh thần hữu ích là CPython tránh quét tất cả các vùng chứa được theo dõi trên mỗi bộ sưu tập.
## 10.8 Ngưỡng thu thập
các`gc`mô-đun hiển thị các ngưỡng:```python
import gc
print(gc.get_threshold())
```Ngưỡng giúp quyết định khi nào việc thu thập theo chu kỳ tự động sẽ chạy.
Bạn có thể thay đổi chúng:```python
gc.set_threshold(700, 10, 10)
```Bạn có thể buộc một bộ sưu tập:```python
gc.collect()
```Bạn có thể tắt tính năng thu thập theo chu kỳ tự động:```python
gc.disable()
```và kích hoạt lại nó:```python
gc.enable()
```Việc tắt bộ sưu tập không tắt tính năng tham chiếu. Các đối tượng có số tham chiếu bằng 0 vẫn bị hủy. Việc tắt bộ thu chỉ vô hiệu hóa tính năng phát hiện chu kỳ tự động.
## 10.9 Cách phát hiện chu kỳ hoạt động
Bộ sưu tập không chỉ đơn giản tìm kiếm các đối tượng có số lượng tham chiếu khác 0. Hầu hết mọi đối tượng sống đều có số lượng tham chiếu khác 0.
Thay vào đó, nó tính toán liệu các tham chiếu giữ cho một nhóm tồn tại chỉ đến từ bên trong nhóm đó hay không.
Một quá trình phát hiện chu kỳ đơn giản hóa:```text
select tracked candidate objects
copy each object's reference count into a temporary field
for each reference from candidate object to candidate object:
subtract one from the target's temporary count
objects with temporary count still positive are reachable from outside
propagate reachability from those externally reachable objects
objects never reached are unreachable cycles
```Ví dụ:```text
outside ---> A ---> B
^ |
| v
D <--- C
```Kể cả nếu`A`, `B`, `C`, Và`D`tạo thành một chu trình, tham chiếu bên ngoài đến`A`làm cho cả nhóm có thể tiếp cận được.
Nhưng:```text
A ---> B
^ |
| v
D <--- C
```không có tài liệu tham khảo bên ngoài có thể được sưu tầm.
## 10.10 Các chu trình có thể tiếp cận không phải là rác
Một chu trình không tự động là rác.```python
a = []
a.append(a)
print(a)
```Đối tượng này có tính tuần hoàn, nhưng có thể truy cập được thông qua tên`a`. Nó phải sống sót.
Bộ sưu tập chỉ lấy lại các chu kỳ không thể truy cập được.
Sự khác biệt này quan trọng đối với cấu trúc dữ liệu. Đồ thị tuần hoàn là bình thường và hợp lệ trong Python. Bộ sưu tập tồn tại để chúng có thể được sử dụng một cách an toàn mà không bị hỏng thủ công trong các trường hợp thông thường.
## 10.11 Phần hoàn thiện
Công cụ hoàn thiện làm phức tạp việc thu thập rác.
Công cụ hoàn thiện thường là một`__del__`phương pháp:```python
class Resource:
def __del__(self):
print("destroying")
```Trình hoàn thiện chạy trong quá trình phá hủy đối tượng. Họ có thể thực thi mã Python. Mã đó có thể truy cập toàn cầu, thay đổi trạng thái, lấy khóa, tạo đối tượng hoặc thậm chí phục hồi đối tượng đang được hoàn thiện.
Phục hồi đối tượng có nghĩa là trình hoàn thiện làm cho đối tượng có thể truy cập lại được:```python
saved = None
class Resurrect:
def __del__(self):
global saved
saved = self
```Điều này làm cho việc thu thập trở nên phức tạp hơn.
CPython hiện đại có các quy tắc cụ thể để xử lý rác theo chu kỳ, nhưng hướng dẫn thực tế rất đơn giản:```text
avoid complex __del__ methods
prefer context managers
prefer weakref.finalize for cleanup hooks
```## 10.12 Trình quản lý bối cảnh tốt hơn cho tài nguyên
Thu gom rác không phải là API quản lý tài nguyên.
Sử dụng`with`để dọn dẹp xác định:```python
with open("data.txt") as f:
data = f.read()
```Thao tác này sẽ đóng tệp khi khối thoát ra.
Đừng dựa vào thời gian thu gom rác:```python
f = open("data.txt")
data = f.read()
f = None
```Trong CPython, việc đếm tham chiếu có thể đóng tệp nhanh chóng. Trong các triển khai khác, việc dọn dẹp có thể diễn ra sau.
Đối với khóa, ổ cắm, tệp, giao dịch, thư mục tạm thời và tay cầm bên ngoài, hãy sử dụng biện pháp kiểm soát trọn đời rõ ràng.
## 10.13`gc.collect`
`gc.collect()`buộc một bộ sưu tập theo chu kỳ.```python
import gc
n = gc.collect()
print(n)
```Giá trị trả về là số lượng đối tượng không thể truy cập được tìm thấy và thu thập.
Bạn có thể yêu cầu một thế hệ cụ thể trong các phiên bản hỗ trợ giao diện đó:```python
gc.collect(0)
gc.collect(1)
gc.collect(2)
```Việc thu thập thủ công rất hữu ích cho:```text
tests
debugging leaks
interactive experiments
memory-sensitive batch phases
controlled benchmarks
```Nó hiếm khi cần thiết trong mã ứng dụng thông thường.
## 10.14`gc.get_objects`
`gc.get_objects()`trả về các đối tượng được theo dõi mà người thu thập đã biết.```python
import gc
objs = gc.get_objects()
print(len(objs))
```Điều này không trả về mọi đối tượng Python trực tiếp. Nó trả về các đối tượng được theo dõi bởi trình thu gom rác tuần hoàn.
Nhiều vật thể nguyên tử có thể vắng mặt.
Sử dụng nó để gỡ lỗi đồ thị đối tượng và rò rỉ bộ nhớ, không phải cho logic ứng dụng thông thường.
## 10,15`gc.get_referrers`
`gc.get_referrers(obj)`trả về các đối tượng tham chiếu trực tiếp đến`obj`.
```python
import gc
x = []
refs = gc.get_referrers(x)
print(refs)
```Điều này có thể giúp giải thích tại sao một vật thể vẫn còn sống.
Nhưng nó phải được sử dụng cẩn thận. Việc gọi các hàm gỡ lỗi sẽ tạo ra các tham chiếu và khung tạm thời. Những điều này có thể xuất hiện trong kết quả.`gc.get_referrers`cũng tiết lộ chi tiết thực hiện. Nó có thể hiển thị các khung ngăn xếp, từ điển, danh sách và các đối tượng bên trong.
## 10.16`gc.get_referents`
`gc.get_referents(obj)`trả về các đối tượng được tham chiếu trực tiếp bởi`obj`.
```python
import gc
x = [[1], [2]]
print(gc.get_referents(x))
```Đối với một danh sách, điều này bao gồm các phần tử của nó.
Đối với một lệnh, điều này có thể bao gồm các khóa và giá trị.
Hàm này sử dụng cùng một hỗ trợ truyền tải mà bộ sưu tập sử dụng. Nó nhìn thấy các tham chiếu cấp GC, không nhất thiết là tất cả các mối quan hệ ngữ nghĩa mà một lập trình viên có thể tưởng tượng.
## 10.17 Rác không thể thu gom
Một số đồ vật có thể không thể truy cập được nhưng không thể thu thập ngay lập tức trong một số điều kiện nhất định.
Trong lịch sử, các chu kỳ chứa các phần cuối cùng đặc biệt khó khăn. CPython hiện đại xử lý nhiều trường hợp này tốt hơn, nhưng các đối tượng không thể thu thập vẫn có thể xuất hiện với các loại tiện ích mở rộng hoặc hành vi hoàn thiện bất thường.
các`gc`mô-đun hiển thị:```python
import gc
print(gc.garbage)
```Khi gỡ lỗi, bạn có thể bật cờ gỡ lỗi:```python
gc.set_debug(gc.DEBUG_SAVEALL)
```Với`DEBUG_SAVEALL`, các đối tượng không thể truy cập được lưu trong`gc.garbage`thay vì được giải thoát. Điều này rất hữu ích cho việc kiểm tra, nhưng nó cố tình rò rỉ cho đến khi bạn xóa danh sách.
## 10.18 Tài liệu tham khảo yếu
Tham chiếu yếu cho phép quan sát một đối tượng mà không giữ cho nó tồn tại.```python
import weakref
class User:
pass
u = User()
r = weakref.ref(u)
print(r()) # object
del u
print(r()) # None
```Tham chiếu yếu không làm tăng số lượng tham chiếu mạnh của mục tiêu.
Tham chiếu yếu rất hữu ích cho bộ nhớ đệm, danh sách người quan sát, liên kết chính và siêu dữ liệu phụ trợ.
Con trỏ cha thường có thể yếu:```python
import weakref
class Node:
def __init__(self):
self.children = []
self.parent = None
root = Node()
child = Node()
child.parent = weakref.ref(root)
root.children.append(child)
```Điều này tránh hình thành một chu kỳ mạnh mẽ thông qua các liên kết cha mẹ và con cái.
## 10.19`weakref.finalize`
`weakref.finalize`đăng ký mã dọn dẹp mà không đặt logic dọn dẹp trực tiếp vào`__del__`.
```python
import weakref
class Resource:
pass
def cleanup(name):
print("cleaning", name)
r = Resource()
finalizer = weakref.finalize(r, cleanup, "resource")
```Khi`r`trở nên không thể truy cập được, trình hoàn thiện có thể chạy.
Điều này thường an toàn hơn việc viết phức tạp`__del__`các phương thức vì trình hoàn thiện tách trạng thái dọn dẹp khỏi đối tượng đang được hoàn thiện.
Tuy nhiên, các tài nguyên bên ngoài thường phải được quản lý bằng trình quản lý bối cảnh khi có thể.
## 10.20 Khung, dấu vết và chu kỳ
Các khung và truy nguyên thường tạo ra các chu kỳ.
Ví dụ:```python
def f():
x = []
raise RuntimeError
try:
f()
except RuntimeError as exc:
saved = exc
```Một ngoại lệ có thể giữ lại dấu vết. Truy nguyên có thể giữ khung. Khung chứa các biến cục bộ. Các biến cục bộ có thể chứa ngoại lệ hoặc các đối tượng liên quan đến nó.
Về mặt khái niệm:```text
exception
traceback
frame
locals
exception
```Điều này có thể giữ cho đồ thị đối tượng lớn tồn tại.
Python hiện đại xóa một số trạng thái ngoại lệ mạnh mẽ hơn so với các phiên bản cũ hơn, nhưng các ngoại lệ và dấu vết được lưu trữ vẫn có thể giữ lại bộ nhớ.
Giảm thiểu chung:```python
try:
...
except Exception as exc:
...
finally:
exc = None
```hoặc tránh lưu trữ dấu vết lâu hơn mức cần thiết.
## 10.21 Đóng cửa và chu kỳ
Việc đóng cửa cũng có thể tạo ra chu kỳ.```python
def make_func():
items = []
def add(x):
items.append(x)
return items
return add
```Hàm bên trong tham chiếu đến một ô. Các tham chiếu ô`items`.
Các bao đóng phức tạp hơn có thể tham chiếu các đối tượng tham chiếu lại hàm.
Chu kỳ thông qua các hàm, bao đóng, phiên bản và lệnh gọi lại là phổ biến trong các chương trình thực.
Trình thu thập xử lý nhiều trong số chúng, nhưng việc hiểu biểu đồ sẽ giúp ích khi bộ nhớ tăng đột ngột.
## 10.22 Các loại tiện ích mở rộng và Hỗ trợ GC
Loại tiện ích mở rộng C sở hữu các tham chiếu đến đối tượng Python có thể cần hỗ trợ GC theo chu kỳ.
Các phần bắt buộc thường bao gồm:```text
Py_TPFLAGS_HAVE_GC
tp_traverse
tp_clear
GC-aware allocation
GC-aware deallocation
```Hàm truyền tải cho bộ sưu tập biết đối tượng mà đối tượng này tham chiếu:```c
static int
Box_traverse(BoxObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->value);
return 0;
}
```Một hàm rõ ràng sẽ phá vỡ các tham chiếu trong quá trình thu thập:```c
static int
Box_clear(BoxObject *self)
{
Py_CLEAR(self->value);
return 0;
}
```Người giải quyết phải bỏ theo dõi đối tượng và xóa các tham chiếu thuộc sở hữu một cách an toàn.
Hình dạng khái niệm:```c
static void
Box_dealloc(BoxObject *self)
{
PyObject_GC_UnTrack(self);
Box_clear(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
```Điều này được đơn giản hóa. Mã mở rộng thực phải tuân theo các yêu cầu API C chính xác.
## 10,23`Py_VISIT`
`Py_VISIT`được sử dụng bên trong các hàm truyền tải.```c
static int
Node_traverse(NodeObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->left);
Py_VISIT(self->right);
Py_VISIT(self->parent);
return 0;
}
```Nó kiểm tra xem trường này có phải là NULL hay không và chuyển nó cho khách truy cập.
Một hàm truyền tải chính xác phải truy cập mọi tham chiếu đối tượng Python có trong đó có thể tham gia vào các chu trình.
Thiếu một trường có thể khiến người thu thập không nhìn thấy được chu trình.
## 10,24`Py_CLEAR`
`Py_CLEAR`xóa một tham chiếu sở hữu một cách an toàn.
Một hoạt động rõ ràng ngây thơ:```c
Py_DECREF(self->value);
self->value = NULL;
```có một vấn đề tế nhị. các`Py_DECREF`có thể chạy mã tùy ý thông qua bộ hoàn thiện. Mã đó có thể quan sát`self`trước`self->value`đã được đặt thành`NULL`.
`Py_CLEAR`đặt trường thành`NULL`đầu tiên, sau đó giảm tham chiếu cũ.
Về mặt khái niệm:```c
tmp = self->value;
self->value = NULL;
Py_XDECREF(tmp);
```Sử dụng`Py_CLEAR`khi phá vỡ các tham chiếu bên trong các thùng chứa hoặc bộ giải phóng.
## 10.25 Bộ sưu tập và Hiệu suất
Bộ thu thập tuần hoàn có chi phí. Nó quét các vùng chứa được theo dõi và theo dõi các tài liệu tham khảo.
Hầu hết các chương trình nên để nó được kích hoạt. Nhưng một số khối lượng công việc có thể điều chỉnh nó:```text
short-lived batch jobs
allocation-heavy parsers
large object graph construction
scientific pipelines
services with known allocation phases
```Ví dụ:```python
import gc
gc.disable()
try:
build_large_graph()
finally:
gc.enable()
gc.collect()
```Mẫu này có thể hữu ích khi bạn biết một giai đoạn tạo ra nhiều vùng chứa không có chu kỳ. Nó cũng có thể bị tổn thương nếu chu kỳ tích lũy.
Đo trước khi điều chỉnh.
## 10.26 Rò rỉ bộ nhớ so với tài liệu tham khảo được giữ lại
Không phải tất cả sự tăng trưởng bộ nhớ đều bị rò rỉ theo nghĩa C.
Một chương trình có thể vô tình giữ lại các tài liệu tham khảo:```python
cache = []
def handle(request):
cache.append(request)
```Bộ sưu tập không thể giải phóng các đối tượng có thể truy cập được. Nếu như`cache`tiếp tục phát triển, những đối tượng đó vẫn còn sống.
Rò rỉ cấp độ C thực sự có nghĩa là bộ nhớ hoặc tài liệu tham khảo bị mất khi triển khai.
Lỗi lưu giữ ở cấp độ Python có nghĩa là chương trình vẫn có các tham chiếu có thể truy cập được đến các đối tượng mà nó không còn cần nữa.
Việc gỡ lỗi bộ nhớ thường bắt đầu bằng cách hỏi:```text
Is the object unreachable but not collected?
Or is something still referring to it?
```Sử dụng`gc.get_referrers`, tracemalloc, công cụ biểu đồ đối tượng và kiểm tra đống để trả lời điều đó.
## 10.27 Các mẫu chu kỳ phổ biến
Các nguồn chu kỳ phổ biến bao gồm:
| Mẫu | Hình dạng |
| ---------------------- | ------------------------------------------------ |
| Liên kết cha mẹ và con cái | cha mẹ -> con -> cha mẹ |
| Danh sách liên kết đôi | nút A -> nút B -> nút A |
| Cấu trúc đồ thị | chu kỳ tùy ý |
| Cuộc gọi lại của người quan sát | đối tượng -> gọi lại -> phương thức ràng buộc -> đối tượng |
| Ngoại lệ | ngoại lệ -> truy nguyên -> khung -> người dân địa phương |
| Đóng cửa | hàm -> ô đóng -> đối tượng -> hàm |
| Mô tả | lớp -> mô tả -> trạng thái liên quan đến lớp |
| Đối tượng mở rộng C | đối tượng gốc -> đối tượng Python -> trình bao bọc gốc |
Chu kỳ là bình thường. Vấn đề là liệu các chu trình không thể truy cập có thể được thu thập hay không và liệu chúng có chứa các tài nguyên bên ngoài cần được dọn dẹp xác định hay không.
## 10.28 Quy tắc thực tế
Đối với mã Python:
| Tình huống | Cách tiếp cận ưa thích |
| ---------------------- | -------------------------------------------------- |
| Nguồn lực bên ngoài | Sử dụng`with`|
| Con trỏ gốc | Coi như`weakref`|
| Bộ nhớ đệm | Sử dụng`weakref.WeakValueDictionary`khi thích hợp |
| Ngoại lệ tồn tại lâu dài | Tránh giữ lại truy nguyên một cách không cần thiết |
| Gỡ lỗi bộ nhớ | Sử dụng`gc`, `tracemalloc`và kiểm tra người giới thiệu |
| Móc dọn dẹp | Thích người quản lý bối cảnh hoặc`weakref.finalize`|
| Giai đoạn phân bổ lớn | Chỉ điều chỉnh GC sau khi đo |
Đối với mã mở rộng C:
| Tình huống | Kỷ luật bắt buộc |
| ------------------------------- | --------------------------------- |
| Loại sở hữu tài liệu tham khảo Python | Thực hiện phân bổ hợp lý |
| Loại có thể hình thành chu kỳ | Thêm hỗ trợ GC |
| Loại hỗ trợ GC | Thực hiện`tp_traverse`chính xác |
| Loại tham gia sưu tập | Thực hiện`tp_clear`chính xác |
| Phá vỡ tài liệu tham khảo | Sử dụng`Py_CLEAR`|
| Đi ngang qua các trường | Sử dụng`Py_VISIT`|
| Phân bổ đối tượng GC | Bỏ theo dõi trước khi xóa |
## 10.29 Mô hình tinh thần
Sử dụng mô hình này:```text
Reference counting handles local lifetime.
Cyclic GC handles unreachable container cycles.
The collector only sees tracked objects.
Tracked objects must describe their outgoing references.
Reachable cycles remain alive.
Unreachable cycles can be collected.
Finalizers and extension types complicate collection.
Resource lifetime should be explicit.
```Mô hình này giải thích tại sao hầu hết các đối tượng CPython biến mất nhanh chóng, tại sao một số cấu trúc tuần hoàn tồn tại cho đến khi có một bộ sưu tập, tại sao`__del__`cần được quan tâm và tại sao các loại tiện ích mở rộng C phải tham gia vào giao thức GC khi chúng sở hữu các tham chiếu Python.
##10:30 Tóm tắt
Trình thu gom rác của CPython tồn tại vì tính tham chiếu không thể lấy lại chu kỳ. Nó theo dõi các đối tượng giống như vùng chứa, phân tích các tham chiếu giữa chúng, tìm các nhóm không thể truy cập và xóa chúng một cách an toàn.
Bộ sưu tập có tính chất thế hệ, có thể định cấu hình thông qua`gc`module và gắn chặt với mô hình đối tượng. Các lập trình viên Python chủ yếu cần hiểu các chu trình, phần hoàn thiện, tham chiếu yếu và thời gian tồn tại của tài nguyên. Tác giả tiện ích mở rộng C phải thực hiện truyền tải và xóa chính xác khi loại của họ có thể tham gia vào chu kỳ.