Jak Python zarządza pamięcią?
Python używa dwóch mechanizmów:
- Reference Counting – podstawowy mechanizm, działa automatycznie
- Garbage Collector – usuwa obiekty w cyklicznych referencjach
Jeśli chcesz poznać narzędzia do profilowania pamięci, sprawdź Profilowanie wydajności w Pythonie.
Reference Counting
Każdy obiekt w Pythonie ma licznik referencji. Gdy licznik spada do zera, obiekt jest natychmiast usuwany.
Jak to działa
Python1 2 3 4 5 6 7 8 9 10 11 12import sys a = [1, 2, 3] # Obiekt lista ma 1 referencję (zmienna a) print(sys.getrefcount(a)) # 2 (a + tymczasowa referencja w getrefcount) b = a # Druga referencja print(sys.getrefcount(a)) # 3 del b # Usunięcie referencji print(sys.getrefcount(a)) # 2 del a # Ostatnia referencja usunięta - obiekt może być zwolniony
Kiedy reference counting nie wystarcza?
Reference counting nie radzi sobie z cyklicznymi referencjami:
Python1 2 3 4 5 6 7 8 9 10 11 12 13class Node: def __init__(self, value): self.value = value self.next = None # Cykliczna referencja a = Node(1) b = Node(2) a.next = b b.next = a # Cykl! del a del b # Obiekty nadal istnieją w pamięci - cykl!
Tutaj wchodzi garbage collector.
Garbage Collector (GC)
GC wykrywa i usuwa obiekty w cyklicznych referencencjach.
Podstawowe informacje o GC
Python1 2 3 4 5import gc # Statystyki GC print(gc.get_count()) # (generation0, generation1, generation2) print(gc.get_stats()) # Szczegółowe statystyki
Generacje GC
GC używa 3 generacji:
- Generation 0 – nowe obiekty
- Generation 1 – obiekty, które przeżyły jedną kolekcję
- Generation 2 – obiekty, które przeżyły wiele kolekcji
Python1 2 3 4 5 6 7 8import gc # Ręczne uruchomienie GC collected = gc.collect() print(f"Zwolniono {collected} obiektów") # GC tylko dla generation 0 gc.collect(0)
Wyłączanie/załączanie GC
Czasami warto tymczasowo wyłączyć GC dla wydajności:
Python1 2 3 4 5 6 7 8 9 10 11import gc # Wyłącz GC gc.disable() # Kod krytyczny wydajnościowo # ... # Włącz z powrotem gc.enable() gc.collect() # Uruchom kolekcję
weakref – słabe referencje
weakref pozwala na referencję do obiektu bez zwiększania jego licznika referencji. Przydatne do cache'owania i obserwatorów.
Podstawowe użycie
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23import weakref import sys class Data: def __init__(self, value): self.value = value def __repr__(self): return f"Data({self.value})" # Normalna referencja obj = Data(42) print(sys.getrefcount(obj)) # 2 # Słaba referencja weak_ref = weakref.ref(obj) print(sys.getrefcount(obj)) # Nadal 2! # Dostęp przez słabą referencję print(weak_ref()) # Data(42) del obj # Obiekt zwolniony print(weak_ref()) # None - obiekt już nie istnieje
WeakValueDictionary – cache ze słabymi referencjami
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25import weakref class ExpensiveObject: def __init__(self, key): self.key = key # Symulacja kosztownej operacji self.data = list(range(1000000)) def __repr__(self): return f"ExpensiveObject({self.key})" # Cache z automatycznym usuwaniem cache = weakref.WeakValueDictionary() def get_object(key): if key not in cache: print(f"Tworzenie nowego obiektu dla {key}") cache[key] = ExpensiveObject(key) return cache[key] obj1 = get_object("a") # Tworzenie nowego obiektu obj2 = get_object("a") # Użycie z cache del obj1, obj2 # Obiekty usunięte print(len(cache)) # 0 - automatycznie zwolnione z cache
Monitorowanie użycia pamięci
sys.getsizeof()
Python1 2 3 4import sys data = [1, 2, 3, 4, 5] print(sys.getsizeof(data)) # Rozmiar obiektu w bajtach
memory_profiler
Python1 2 3 4 5 6 7 8from memory_profiler import profile @profile def moja_funkcja(): data = list(range(1000000)) return sum(data) moja_funkcja()
tracemalloc
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19import tracemalloc tracemalloc.start() # Twój kod data = [x**2 for x in range(1000000)] # Statystyki current, peak = tracemalloc.get_traced_memory() print(f"Current: {current / 1024 / 1024:.2f} MB") print(f"Peak: {peak / 1024 / 1024:.2f} MB") # Top 10 alokacji snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') for stat in top_stats[:10]: print(stat) tracemalloc.stop()
Optymalizacja pamięci
1. Unikaj niepotrzebnych kopii
Python1 2 3 4 5 6 7# ❌ Niepotrzebna kopia data = list(range(1000000)) copy = data[:] # Kopia całej listy # ✅ Użyj referencji lub slice tylko gdy potrzeba data = list(range(1000000)) reference = data # Tylko referencja
2. Używaj generatorów dla dużych zbiorów
Generatory to doskonały sposób na oszczędzanie pamięci. Więcej o generatorach znajdziesz w Generatory i przetwarzanie danych:
Python1 2 3 4 5 6 7 8 9 10# ❌ Ładuje wszystko do pamięci def read_file_lines(filename): with open(filename) as f: return f.readlines() # Cały plik w pamięci # ✅ Generator - jedna linia na raz def read_file_lines(filename): with open(filename) as f: for line in f: yield line
3. Usuwaj duże obiekty, gdy nie są potrzebne
Python1 2 3 4 5 6 7 8 9# Duże dane large_data = list(range(10_000_000)) # Przetwarzanie result = process(large_data) # Usuń duże dane, jeśli nie są już potrzebne del large_data gc.collect() # Wymuś zwolnienie pamięci
4. Używaj slots dla klas z wieloma instancjami
Python1 2 3 4 5 6 7 8 9 10 11 12 13# Bez __slots__ class Point: def __init__(self, x, y): self.x = x self.y = y # Z __slots__ - mniej pamięci class PointOptimized: __slots__ = ['x', 'y'] def __init__(self, x, y): self.x = x self.y = y
Najlepsze praktyki
1. Pozwól Python zarządzać pamięcią
W większości przypadków Python robi to dobrze automatycznie.
2. Profiluj przed optymalizacją
Używaj memory_profiler lub tracemalloc do znajdowania problemów.
3. Używaj weakref dla cache'ów i obserwatorów
Unikaj cyklicznych referencji w cache'ach.
4. Usuwaj duże obiekty explicite
Jeśli wiesz, że obiekt nie jest już potrzebny, użyj del.
5. Monitoruj w produkcji
Używaj narzędzi do monitorowania pamięci w aplikacjach produkcyjnych.
Podsumowanie
Zarządzanie pamięcią w Pythonie jest automatyczne, ale zrozumienie mechanizmów pomaga w optymalizacji:
- ✅ Reference Counting – podstawowy mechanizm
- ✅ Garbage Collector – usuwa cykliczne referencje
- ✅ weakref – słabe referencje bez zwiększania licznika
- ✅ Profilowanie – znajdowanie problemów z pamięcią
- ✅ Optymalizacja – generatory, slots, usuwanie dużych obiektów
Co dalej?
Rozwijaj umiejętności optymalizacji:
- Profilowanie wydajności w Pythonie – narzędzia do analizy pamięci (memory_profiler, tracemalloc)
- Zaawansowane techniki w Pythonie – więcej technik optymalizacji
- Generatory i przetwarzanie danych – oszczędzanie pamięci z generatorami
- Wielowątkowość a wieloprocesowość – wpływ wieloprocesowości na pamięć
Pamiętaj: Python zarządza pamięcią automatycznie, ale zrozumienie mechanizmów pozwala na lepszą optymalizację!



