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 hot độ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 vn tham chiếu ln nhau:```text
list A ---> list B
list B ---> list A
```S lượng tham chiếu ca h vn khác không. Nhưng không có mã Python trc tiếp nào có th tiếp cn được chúng.

Vic đếm tham chiếu cho thy quyn s hu cc b. B sưu tp rác nhìn thy kh năng tiếp cn.

## 10.2 Người thu gom rác theo dõi những gì

CPython không cn theo dõi mi đối tượng trong trình thu gom rác tun hoàn.

Các đối tượng không th cha 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 gm nhiu s nguyên, s float và chui đơn gin.

B sưu tp ch yếu theo dõi các đối tượng ging như thùng cha:```text
list
dict
set
tuple containing references
function
class
instance
frame
generator
coroutine
traceback
some extension objects
```Mt đối tượng cn theo dõi GC khi nó có th tham gia vào mt 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 to ra các chu trình vùng cha.

Nhưng:```python
x = []
x.append(x)
```Danh sách tr đến chính nó. Đây là mt chu k.

## 10.3 Tự chu kỳ

Chu trình tham chiếu đơn gin nht là chu trình t chu trình:```python
x = []
x.append(x)
```Cu trúc là:```text
x ---> list
       ^  |
       |  |
       +--+
```Bây gi xóa tên bên ngoài:```python
del x
```Danh sách vn cha mt tham chiếu đến chính nó. S tham chiếu ca nó vn  trên 0.

Không có biến chương trình nào có th tiếp cn được nó, nhưng ch tính tham chiếu thôi thì không th phá hy được nó. Trình thu gom rác tun hoàn phi phát hin ra nó.

## 10.4 Chu kỳ đa đối tượng

Chu k thường liên quan đến nhiu đố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
```Biu đồ tr nên không th truy cp được nhưng các tham chiếu vn còn bên trong biu đồ:```text
root node ---> children list ---> child node
    ^                              |
    |                              |
    +----------- parent -----------+
```Mu 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 ngoi l.

## 10.5 Collector hoạt động trên Container

B thu thp tun hoàn ch cn suy lun v các đối tượng có th tr đến các đối tượng khác.

Nó hi tng đối tượng được theo dõi:```text
Which Python objects do you reference?
``` cp độ C, các loi tin ích m rng tr li thông qua h tr truyn ti. Loi nhn biết GC cung cp chc năng truyn ti truy cp các tham chiếu Python có cha.

V mt khái nim:```c
static int
Node_traverse(NodeObject *self, visitproc visit, void *arg)
{
    Py_VISIT(self->parent);
    Py_VISIT(self->children);
    return 0;
}
```B sưu tp s dng điu này để đi qua đồ th đối tượng.

Nếu mt đối tượng m rng s hu các tham chiếu Python và có th là mt phn ca chu trình thì nó phi tham gia vào giao thc 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

Mt đối tượng có th được theo dõi hoc không b theo dõi bi b thu thp tun hoàn.

Được theo dõi có nghĩa là người thu gom có ​​th kim tra nó trong quá trình thu thp.

Không b theo dõi có nghĩa là b thu thp b qua nó để phát hin chu k.

Bn có th kim tra điu này t Python:```python
import gc

print(gc.is_tracked([]))
print(gc.is_tracked(123))
print(gc.is_tracked("hello"))
```Đầu ra đin hình trên CPython có th cho thy các thùng cha đượ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 dng ti ưu hóa. Ví d: mt s vùng cha có th không b theo dõi khi chúng ch cha các đối tượng nguyên t.

Quy tc quan trng mang tính khái nim:```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 ca CPython mang tính thế h.

Ý tưởng này da trên mt quan sát chung: hu hết các vt th đều chết tr. Các đối tượng tn ti qua nhiu b sưu tp có kh năng tn ti lâu hơn.

Mt nhóm nhà sưu tp thế h đã theo dõi các đồ vt theo độ tui. Thế h tr được thu thp thường xuyên hơn. Các thế h cũ được thu thp ít thường xuyên hơn.

V mt khái nim:```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 bn CPython. Mô hình tinh thn hu ích là CPython tránh quét tt c các vùng cha được theo dõi trên mi b sưu tp.

## 10.8 Ngưỡng thu thập

các`gc`mô-đun hin th các ngưỡng:```python
import gc

print(gc.get_threshold())
```Ngưỡng giúp quyết định khi nào vic thu thp theo chu k t động s chy.

Bn có th thay đổi chúng:```python
gc.set_threshold(700, 10, 10)
```Bn có th buc mt b sưu tp:```python
gc.collect()
```Bn có th tt tính năng thu thp theo chu k t động:```python
gc.disable()
```và kích hot li nó:```python
gc.enable()
```Vic tt b sưu tp không tt tính năng tham chiếu. Các đối tượng có s tham chiếu bng 0 vn b hy. Vic tt b thu ch vô hiu hóa tính năng phát hin chu k t động.

## 10.9 Cách phát hiện chu kỳ hoạt động

B sưu tp không ch đơn gin tìm kiếm các đối tượng có s lượng tham chiếu khác 0. Hu hết mi đối tượng sng đều có s lượng tham chiếu khác 0.

Thay vào đó, nó tính toán liu các tham chiếu gi cho mt nhóm tn ti ch đến t bên trong nhóm đó hay không.

Mt quá trình phát hin chu k đơn gin 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`to thành mt chu trình, tham chiếu bên ngoài đến`A`làm cho c nhóm có th tiếp cn được.

Nhưng:```text
A ---> B
^     |
|     v
D <--- C
```không có tài liu tham kho bên ngoài có th được sưu tm.

## 10.10 Các chu trình có thể tiếp cận không phải là rác

Mt 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 tun hoàn, nhưng có th truy cp được thông qua tên`a`. Nó phi sng sót.

B sưu tp ch ly li các chu k không th truy cp được.

S khác bit này quan trng đối vi cu trúc d liu. Đồ th tun hoàn là bình thường và hp l trong Python. B sưu tp tn ti để chúng có th được s dng mt cách an toàn mà không b hng th công trong các trường hp thông thường.

## 10.11 Phần hoàn thiện

Công c hoàn thin làm phc tp vic thu thp rác.

Công c hoàn thin thường là mt`__del__`phương pháp:```python
class Resource:
    def __del__(self):
        print("destroying")
```Trình hoàn thin chy trong quá trình phá hy đối tượng. H có th thc thi mã Python. Mã đó có th truy cp toàn cu, thay đổi trng thái, ly khóa, to đối tượng hoc thm chí phc hi đối tượng đang được hoàn thin.

Phc hi đối tượng có nghĩa là trình hoàn thin làm cho đối tượng có th truy cp li được:```python
saved = None

class Resurrect:
    def __del__(self):
        global saved
        saved = self
```Điu này làm cho vic thu thp tr nên phc tp hơn.

CPython hin đại có các quy tc c th để x lý rác theo chu k, nhưng hướng dn thc tế rt đơn gin:```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 phi là API qun lý tài nguyên.

S dng`with`để dn dp xác định:```python
with open("data.txt") as f:
    data = f.read()
```Thao tác này s đóng tp khi khi thoát ra.

Đừng da vào thi gian thu gom rác:```python
f = open("data.txt")
data = f.read()
f = None
```Trong CPython, vic đếm tham chiếu có th đóng tp nhanh chóng. Trong các trin khai khác, vic dn dp có th din ra sau.

Đối vi khóa,  cm, tp, giao dch, thư mc tm thi và tay cm bên ngoài, hãy s dng bin pháp kim soát trn đời rõ ràng.

## 10.13`gc.collect`

`gc.collect()`buc mt b sưu tp 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 cp được tìm thy và thu thp.

Bn có th yêu cu mt thế h c th trong các phiên bn h tr giao din đó:```python
gc.collect(0)
gc.collect(1)
gc.collect(2)
```Vic thu thp th công rt hu ích cho:```text
tests
debugging leaks
interactive experiments
memory-sensitive batch phases
controlled benchmarks
```Nó hiếm khi cn thiết trong mã ng dng 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 thp đã biết.```python
import gc

objs = gc.get_objects()
print(len(objs))
```Điu này không tr v mi đối tượng Python trc tiếp. Nó tr v các đối tượng được theo dõi bi trình thu gom rác tun hoàn.

Nhiu vt th nguyên t có th vng mt.

S dng nó để g li đồ th đối tượng và rò r b nh, không phi cho logic ng dng thông thường.

## 10,15`gc.get_referrers`

`gc.get_referrers(obj)`tr v các đối tượng tham chiếu trc tiếp đến`obj`.

```python
import gc

x = []
refs = gc.get_referrers(x)
print(refs)
```Điu này có th giúp gii thích ti sao mt vt th vn còn sng.

Nhưng nó phi được s dng cn thn. Vic gi các hàm g li s to ra các tham chiếu và khung tm thi. Nhng điu này có th xut hin trong kết qu.`gc.get_referrers`cũng tiết l chi tiết thc hin. Nó có th hin th các khung ngăn xếp, t đin, 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 trc tiếp bi`obj`.

```python
import gc

x = [[1], [2]]
print(gc.get_referents(x))
```Đối vi mt danh sách, điu này bao gm các phn t ca nó.

Đối vi mt lnh, điu này có th bao gm các khóa và giá tr.

Hàm này s dng cùng mt h tr truyn ti mà b sưu tp s dng. Nó nhìn thy các tham chiếu cp GC, không nht thiết là tt c các mi quan h ng nghĩa mà mt lp trình viên có th tưởng tượng.

## 10.17 Rác không thể thu gom

Mt s đồ vt có th không th truy cp được nhưng không th thu thp ngay lp tc trong mt s điu kin nht định.

Trong lch s, các chu k cha các phn cui cùng đặc bit khó khăn. CPython hin đại x lý nhiu trường hp này tt hơn, nhưng các đối tượng không th thu thp vn có th xut hin vi các loi tin ích m rng hoc hành vi hoàn thin bt thường.

các`gc`mô-đun hin th:```python
import gc

print(gc.garbage)
```Khi g li, bn có th bt c g li:```python
gc.set_debug(gc.DEBUG_SAVEALL)
```Vi`DEBUG_SAVEALL`, các đối tượng không th truy cp được lưu trong`gc.garbage`thay vì được gii thoát. Điu này rt hu ích cho vic kim tra, nhưng nó c tình rò r cho đến khi bn 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 mt đối tượng mà không gi cho nó tn ti.```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 mnh ca mc tiêu.

Tham chiếu yếu rt hu ích cho b nh đệm, danh sách người quan sát, liên kết chính và siêu d liu 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)
```Điu này tránh hình thành mt chu k mnh 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ã dn dp mà không đặt logic dn dp trc 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 cp được, trình hoàn thin có th chy.

Điu này thường an toàn hơn vic viết phc tp`__del__`các phương thc vì trình hoàn thin tách trng thái dn dp khi đối tượng đang được hoàn thin.

Tuy nhiên, các tài nguyên bên ngoài thường phi được qun lý bng trình qun lý bi cnh khi có th.

## 10.20 Khung, dấu vết và chu kỳ

Các khung và truy nguyên thường to ra các chu k.

Ví d:```python
def f():
    x = []
    raise RuntimeError

try:
    f()
except RuntimeError as exc:
    saved = exc
```Mt ngoi l có th gi li du vết. Truy nguyên có th gi khung. Khung cha các biến cc b. Các biến cc b có th cha ngoi l hoc các đối tượng liên quan đến nó.

V mt khái nim:```text
exception
    traceback
        frame
            locals
                exception
```Điu này có th gi cho đồ th đối tượng ln tn ti.

Python hin đại xóa mt s trng thái ngoi l mnh m hơn so vi các phiên bn cũ hơn, nhưng các ngoi l và du vết được lưu tr vn có th gi li b nh.

Gim thiu chung:```python
try:
    ...
except Exception as exc:
    ...
finally:
    exc = None
```hoc tránh lưu tr du vết lâu hơn mc cn thiết.

## 10.21 Đóng cửa và chu kỳ

Vic đóng ca cũng có th to 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 mt ô. Các tham chiếu ô`items`.

Các bao đóng phc tp hơn có th tham chiếu các đối tượng tham chiếu li hàm.

Chu k thông qua các hàm, bao đóng, phiên bn và lnh gi li là ph biến trong các chương trình thc.

Trình thu thp x lý nhiu trong s chúng, nhưng vic hiu biu đồ s giúp ích khi b nh tăng đột ngt.

## 10.22 Các loại tiện ích mở rộng và Hỗ trợ GC

Loi tin ích m rng C s hu các tham chiếu đến đối tượng Python có th cn h tr GC theo chu k.

Các phn bt buc thường bao gm:```text
Py_TPFLAGS_HAVE_GC
tp_traverse
tp_clear
GC-aware allocation
GC-aware deallocation
```Hàm truyn ti cho b sưu tp 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;
}
```Mt hàm rõ ràng s phá v các tham chiếu trong quá trình thu thp:```c
static int
Box_clear(BoxObject *self)
{
    Py_CLEAR(self->value);
    return 0;
}
```Người gii quyết phi b theo dõi đối tượng và xóa các tham chiếu thuc s hu mt cách an toàn.

Hình dng khái nim:```c
static void
Box_dealloc(BoxObject *self)
{
    PyObject_GC_UnTrack(self);
    Box_clear(self);
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```Điu này được đơn gin hóa. Mã m rng thc phi tuân theo các yêu cu API C chính xác.

## 10,23`Py_VISIT`

`Py_VISIT`được s dng bên trong các hàm truyn ti.```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ó kim tra xem trường này có phi là NULL hay không và chuyn nó cho khách truy cp.

Mt hàm truyn ti chính xác phi truy cp mi tham chiếu đối tượng Python có trong đó có th tham gia vào các chu trình.

Thiếu mt trường có th khiến người thu thp không nhìn thy được chu trình.

## 10,24`Py_CLEAR`

`Py_CLEAR`xóa mt tham chiếu s hu mt cách an toàn.

Mt hot động rõ ràng ngây thơ:```c
Py_DECREF(self->value);
self->value = NULL;
```có mt vn đề tế nh. các`Py_DECREF`có th chy mã tùy ý thông qua b hoàn thin. 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 đó gim tham chiếu cũ.

V mt khái nim:```c
tmp = self->value;
self->value = NULL;
Py_XDECREF(tmp);
```S dng`Py_CLEAR`khi phá v các tham chiếu bên trong các thùng cha hoc b gii phóng.

## 10.25 Bộ sưu tập và Hiệu suất

B thu thp tun hoàn có chi phí. Nó quét các vùng cha được theo dõi và theo dõi các tài liu tham kho.

Hu hết các chương trình nên để nó được kích hot. Nhưng mt s khi lượng công vic có th điu chnh 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()
```Mu này có th hu ích khi bn biết mt giai đon to ra nhiu vùng cha không có chu k. Nó cũng có th b tn thương nếu chu k tích lũy.

Đo trước khi điu chnh.

## 10.26 Rò rỉ bộ nhớ so với tài liệu tham khảo được giữ lại

Không phi tt c s tăng trưởng b nh đều b rò r theo nghĩa C.

Mt chương trình có th vô tình gi li các tài liu tham kho:```python
cache = []

def handle(request):
    cache.append(request)
```B sưu tp không th gii phóng các đối tượng có th truy cp được. Nếu như`cache`tiếp tc phát trin, nhng đối tượng đó vn còn sng.

Rò r cp độ C thc s có nghĩa là b nh hoc tài liu tham kho b mt khi trin khai.

Li lưu gi  cp độ Python có nghĩa là chương trình vn có các tham chiếu có th truy cp được đến các đối tượng mà nó không còn cn na.

Vic g li b nh thường bt đầu bng cách hi:```text
Is the object unreachable but not collected?
Or is something still referring to it?
```S dng`gc.get_referrers`, tracemalloc, công c biu đồ đối tượng và kim tra đống để tr li điu đó.

## 10.27 Các mẫu chu kỳ phổ biến

Các ngun chu k ph biến bao gm:

| Mu | Hình dng |
| ---------------------- | ------------------------------------------------ |
| 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 |
| Cu trúc đồ th | chu k tùy ý |
| Cuc gi li ca người quan sát | đối tượng -> gi li -> phương thc ràng buc -> đối tượng |
| Ngoi l | ngoi l -> truy nguyên -> khung -> người dân địa phương |
| Đóng ca | hàm -> ô đóng -> đối tượng -> hàm |
| Mô t | lp -> mô t -> trng thái liên quan đến lp |
| Đối tượng m rng C | đối tượng gc -> đối tượng Python -> trình bao bc gc |

Chu k là bình thường. Vn đề là liu các chu trình không th truy cp có th được thu thp hay không và liu chúng có cha các tài nguyên bên ngoài cn được dn dp xác định hay không.

## 10.28 Quy tắc thực tế

Đối vi mã Python:

| Tình hung | Cách tiếp cn ưa thích |
| ---------------------- | -------------------------------------------------- |
| Ngun lc bên ngoài | S dng`with`|
| Con tr gc | Coi như`weakref`|
| B nh đệm | S dng`weakref.WeakValueDictionary`khi thích hp |
| Ngoi l tn ti lâu dài | Tránh gi li truy nguyên mt cách không cn thiết |
| G li b nh | S dng`gc`, `tracemalloc`và kim tra người gii thiu |
| Móc dn dp | Thích người qun lý bi cnh hoc`weakref.finalize`|
| Giai đon phân b ln | Ch điu chnh GC sau khi đo |

Đối vi mã m rng C:

| Tình hung | K lut bt buc |
| ------------------------------- | --------------------------------- |
| Loi s hu tài liu tham kho Python | Thc hin phân b hp lý |
| Loi có th hình thành chu k | Thêm h tr GC |
| Loi h tr GC | Thc hin`tp_traverse`chính xác |
| Loi tham gia sưu tp | Thc hin`tp_clear`chính xác |
| Phá v tài liu tham kho | S dng`Py_CLEAR`|
| Đi ngang qua các trường | S dng`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 dng 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 gii thích ti sao hu hết các đối tượng CPython biến mt nhanh chóng, ti sao mt s cu trúc tun hoàn tn ti cho đến khi có mt b sưu tp, ti sao`__del__`cn được quan tâm và ti sao các loi tin ích m rng C phi tham gia vào giao thc GC khi chúng s hu các tham chiếu Python.

##10:30 Tóm tắt

Trình thu gom rác ca CPython tn ti vì tính tham chiếu không th ly li chu k. Nó theo dõi các đối tượng ging như vùng cha, phân tích các tham chiếu gia chúng, tìm các nhóm không th truy cp và xóa chúng mt cách an toàn.

B sưu tp có tính cht thế h, có th định cu hình thông qua`gc`module và gn cht vi mô hình đối tượng. Các lp trình viên Python ch yếu cn hiu các chu trình, phn hoàn thin, tham chiếu yếu và thi gian tn ti ca tài nguyên. Tác gi tin ích m rng C phi thc hin truyn ti và xóa chính xác khi loi ca h có th tham gia vào chu k.