0Pricing
Python Academy · Lekcja

Garbage collector i cykliczne odwołania

Proszę poznać sposób, w jaki moduł gc obsługuje cykle odwołań.

Garbage collector i cykliczne odwołania to bezpłatna lekcja Python Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Python Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Python Academy zawiera 4 lekcji w sumie.

Dlaczego zliczanie referencji nie wystarcza

Zliczanie referencji nie może zwolnić obiektów uczestniczących w cyklach referencji — każdy z nich przechowuje referencję do drugiego, więc żaden licznik nie spada do 0.

# Reference cycle: a → b → a
a = {}
b = {"other": a}
a["other"] = b
del a, b
# Both objects are unreachable but refcount > 0
# Only the cyclic GC can collect them

Moduł gc

gc to cykliczny garbage collector. Okresowo skanuje śledzone obiekty w poszukiwaniu nieosiągalnych cykli i je zbiera.

import gc

gc.collect()   # trigger immediately
print(gc.garbage)   # objects with __del__ in cycles (cannot auto-collect)

Zbieranie generacyjne

GC używa trzech generacji. Nowe obiekty trafiają do generacji 0. Obiekty, które przetrwają zbieranie, są przenoszone do wyższej generacji. Generacja 0 jest zbierana najczęściej.

import gc

print(gc.get_count())     # (gen0, gen1, gen2) allocations since last collection
print(gc.get_threshold()) # (700, 10, 10) — thresholds for each generation
gc.set_threshold(1000, 15, 10)  # tune

Wykrywanie cykli za pomocą gc.get_referents

Proszę sprawdzać, do których obiektów odwołuje się dany obiekt — ułatwia to zrozumienie, dlaczego coś nie jest zbierane.

import gc

a = []
b = [a]
a.append(b)

for ref in gc.get_referents(a):
    print(ref)   # shows b

gc.get_objects()

gc.get_objects() zwraca wszystkie obiekty aktualnie śledzone przez GC. Jest to przydatne podczas wyszukiwania wycieków pamięci.

import gc

before = len(gc.get_objects())
create_lots_of_objects()
after = len(gc.get_objects())
print(f"Leaked: {after - before} objects")

Unikanie cykli

Struktury danych należy projektować tak, aby unikać cykli: proszę używać weakrefs dla odwołań zwrotnych, identyfikatorów zamiast bezpośrednich referencji albo jawnie przerywać cykle, zanim obiekty wyjdą poza zakres.

import weakref

class Node:
    def __init__(self, parent):
        # weakref avoids cycle:
        self.parent = weakref.ref(parent)

class Tree:
    def __init__(self):
        self.child = Node(self)   # no hard cycle

__del__ a cykle

Obiekty z __del__, które uczestniczą w cyklu, nie mogą zostać automatycznie zebrane. Zamiast tego trafiają do gc.garbage.

import gc

class Leaky:
    def __del__(self): pass

a = Leaky()
b = Leaky()
a.other = b
b.other = a
del a, b

gc.collect()
print(gc.garbage)   # [Leaky, Leaky] — cannot collect

gc.freeze() — Python 3.7+

gc.freeze() zamraża aktualnie śledzone obiekty, dzięki czemu nigdy nie są zbierane. Jest to przydatne w długo działających procesach po uruchomieniu programu, aby zmniejszyć przerwy powodowane przez GC.

import gc

# After all globals and imports are in place:
gc.freeze()   # no GC scanning for these objects ever again

# Useful for: gunicorn pre-fork, background servers

Wyłączanie i ponowne włączanie GC

W ciasnych pętlach, w których wiadomo, że nie występują cykle, należy wyłączyć cykliczny GC, a następnie ponownie go włączyć i wymusić zbieranie.

import gc

gc.disable()
try:
    result = tight_loop_no_cycles()
finally:
    gc.enable()
    gc.collect()

isenabled i isfinalized

Stan GC można sprawdzić za pomocą gc.isenabled(), a to, czy obiekt został sfinalizowany, za pomocą gc.is_finalized(obj).

import gc

print(gc.isenabled())      # True by default
obj = object()
print(gc.is_finalized(obj))  # False (still alive)

Typowe przyczyny wycieków pamięci

Typowe przyczyny wycieków pamięci w Pythonie to: globalne pamięci podręczne rosnące bez ograniczeń, nieodłączone odbiorniki zdarzeń, cykliczne referencje z __del__ oraz rozszerzenia C, które wyciekają referencje.

# Typical leaks:
global_cache = {}   # grows forever — use lru_cache with maxsize

class EventBus:
    listeners = []
    # Listeners hold references to objects:
    # @classmethod
    # def on(cls, fn): cls.listeners.append(fn)
    # Must call off() to detach

Szybkie sprawdzenie

Co dzieje się z obiektami zawierającymi __del__, które uczestniczą w cyklu referencji?

Podsumowanie

Cykliczny GC obsługuje cykle referencji, z którymi nie radzi sobie zliczanie referencji. Używa trzech generacji i przenosi obiekty, które przetrwały, do wyższych generacji. Cykli należy unikać, korzystając z weakrefs. Obiekty z __del__ należące do cykli trafiają do gc.garbage. Po uruchomieniu programu warto użyć gc.freeze(), aby zmniejszyć przerwy powodowane przez GC w długo działających serwerach.

Często zadawane pytania

Czy lekcja „Garbage collector i cykliczne odwołania” jest bezpłatna?

Tak — pełny tekst „Garbage collector i cykliczne odwołania” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Python Academy, przejdź na CoddyKit PRO. Kurs Python Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Garbage collector i cykliczne odwołania”?

Proszę poznać sposób, w jaki moduł gc obsługuje cykle odwołań. Ćwiczysz Python Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Python Academy?

Nie wymagamy żadnego doświadczenia. Python Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Garbage collector i cykliczne odwołania”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Python Academy?

Tak. Każda lekcja Python Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Zliczanie odwołań w CPythonie
  2. Garbage collector i cykliczne odwołania
  3. Profilowanie za pomocą cProfile i line_profiler
  4. Profilowanie pamięci za pomocą tracemalloc
← Powrót do Python Academy