Garbage Collector w C# — generacje i pułapki

Twoja aplikacja tworzy tysiące obiektów na minutę — a mimo to nie wywołujesz nigdzie free() ani delete i pamięć nie wycieka. Robi to za Ciebie Garbage Collector (GC). Dopóki działa, łatwo o nim zapomnieć — do momentu, gdy w logach pojawia się OutOfMemoryException albo aplikacja „przycina się” co kilka sekund. W tym wpisie zobaczysz, jak GC naprawdę działa: skąd wie, co jest śmieciem, po co mu generacje i kiedy sam potrafisz mu zaszkodzić.
Skąd GC wie, co jest śmieciem?
Obiekty tworzone operatorem new trafiają na zarządzaną stertę (managed heap) — o różnicy między stosem a stertą przeczytasz we wpisie o typach wartościowych i referencyjnych. GC okresowo przechodzi po tzw. korzeniach (roots): polach statycznych, zmiennych lokalnych aktywnych metod na stosach wątków i uchwytach GC. Wszystko, do czego da się dojść od korzeni po łańcuchu referencji, jest „żywe”. Reszta to śmieci.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Person person = new Person(); // "person" (zmienna lokalna) jest korzeniem —
// obiekt na stercie jest osiągalny, więc żywyZauważ konsekwencję: obiekt Person trzyma referencję do stringa Name — dopóki żyje osoba, żyje też jej string. GC zwalnia dopiero całe nieosiągalne grafy obiektów, nie pojedyncze pola.
Generacje 0, 1, 2 — dlaczego GC jest szybki
Skanowanie całej sterty przy każdym czyszczeniu byłoby zbyt kosztowne. GC opiera się więc na obserwacji zwanej hipotezą generacyjną: większość obiektów żyje bardzo krótko (zmienna w metodzie, DTO w jednym żądaniu HTTP). Sterta jest podzielona na generacje:
| Generacja | Co tu trafia | Jak często zbierana |
|---|---|---|
| Gen 0 | Nowe obiekty | Bardzo często, bardzo tanio |
| Gen 1 | Obiekty, które przeżyły zbiórkę Gen 0 | Rzadziej |
| Gen 2 | Obiekty długowieczne (cache, singletony, statyki) | Najrzadziej — pełna zbiórka jest najdroższa |
| LOH | Obiekty ≥ 85 000 bajtów (duże tablice, bufory) | Razem z Gen 2, domyślnie bez kompaktowania |
Obiekt, który przeżyje zbiórkę, awansuje do wyższej generacji. Dzięki temu GC zwykle skanuje tylko młodą, małą część sterty — i to jest cały sekret jego wydajności.
Reguła do zapamiętania: im krócej żyją Twoje obiekty, tym taniej pracuje GC. Drogie są obiekty „w średnim wieku” — te, które przeżywają do Gen 1/2, a potem i tak umierają.
Zobacz GC w akcji — licznik kolekcji
Klasa GC udostępnia m.in. GC.CollectionCount(generacja) — licznik zbiórek danej generacji. Prosty eksperyment: tworzymy milion obiektów, nie zapisując do nich referencji, i co 100 000 iteracji wypisujemy sumę zbiórek.
class Program
{
static void Main()
{
for (int i = 0; i < 1_000_000; i++)
{
var p = new Person { Id = i, Name = i.ToString(), Email = i.ToString() };
if (i % 100_000 == 0)
{
Console.WriteLine("Iteracja: " + i + ", zbiórek GC: " + GetTotalCollections());
}
}
}
static int GetTotalCollections()
{
return GC.CollectionCount(0) +
GC.CollectionCount(1) +
GC.CollectionCount(2);
}
}Każdy obiekt staje się śmieciem natychmiast po utworzeniu (nikt nie trzyma referencji), więc licznik rośnie leniwie — małe obiekty długo mieszczą się w budżecie Gen 0. Wystarczy jednak dodać do klasy pole byte[] Buffer = new byte[100_000]; (czyli każdy obiekt ciągnie za sobą 100 KB — ponad próg LOH!), a liczba zbiórek skoczy o rzędy wielkości. Ten sam program, jedna linijka różnicy — dlatego przy dużych buforach warto znać Span<T> i Memory<T>, które pozwalają pracować na wycinkach pamięci bez nowych alokacji. Z tego samego powodu sklejanie stringów w pętli zastępuje się StringBuilderem.
Kompaktowanie sterty
Po zwolnieniu nieosiągalnych obiektów w stercie zostają „dziury”. GC przesuwa więc żywe obiekty obok siebie (kompaktowanie) i poprawia wszystkie referencje, żeby dalej wskazywały we właściwe miejsca — trochę jak defragmentacja dysku, tylko automatyczna i niewidoczna dla Twojego kodu. Wyjątkiem jest LOH: dużych obiektów domyślnie się nie przesuwa (koszt kopiowania megabajtów byłby zbyt wysoki), przez co LOH może się fragmentować.
Pułapki — tu GC przestaje być magią
Pułapka #1: ręczne GC.Collect()
GC.Collect() wymusza pełną, blokującą zbiórkę wszystkich generacji. Wywoływany „profilaktycznie” psuje dokładnie to, co czyni GC szybkim: sztucznie awansuje krótkotrwałe obiekty do wyższych generacji i zamraża wątki aplikacji. Uzasadnione użycia są rzadkie (np. tuż po zwolnieniu ogromnego, jednorazowego zbioru danych albo w benchmarkach). Jeśli nie umiesz wskazać konkretnego powodu — nie wywołuj go.
Pułapka #2: referencje, o których zapomniałeś
GC nie zwolni niczego, do czego wciąż prowadzi referencja. Wystarczy odkładać obiekty do statycznej listy, żeby „nieśmiertelne” obiekty zapełniły stertę:
static List<Person> people = new List<Person>();
for (int i = 0; i < 1_000_000; i++)
{
// każdy obiekt ma pole byte[100_000] — po ~600 000 iteracji:
// Unhandled exception. System.OutOfMemoryException
people.Add(new Person { Id = i });
}W realnym kodzie tę rolę częściej gra cache bez limitu albo zdarzenie: publisher trzymający referencję do subskrybenta (obiekt.JakiesZdarzenie += Handler; bez późniejszego -=) potrafi utrzymywać przy życiu całe grafy obiektów. To najczęstsze źródło „wycieków pamięci” w języku, który teoretycznie ich nie ma.
Pułapka #3: GC nie zwalnia zasobów niezarządzanych
Plik, połączenie z bazą, gniazdo sieciowe — to zasoby systemu operacyjnego, o których GC wie tylko tyle, że opakowujący je obiekt .NET-owy przestał być osiągalny. Kiedy je zwolni? Nie wiadomo — może za milisekundę, może za minutę. Dlatego typy trzymające takie zasoby implementują IDisposable, a Ty zwalniasz je deterministycznie blokiem using, zamiast liczyć na GC.
Checklist przed code review
- Żadnych
GC.Collect()bez komentarza wyjaśniającego konkretny powód. - Kolekcje statyczne i cache mają politykę usuwania wpisów (limit, TTL).
- Każde
+=na zdarzeniu ma ścieżkę do-=(albo subskrybent żyje krócej niż publisher). - Obiekty
IDisposablezawsze wusing. - Bufory ≥ 85 000 B (LOH!) — przemyślane: pula buforów,
Span<T>, mniejsze porcje.
Podsumowanie
- GC zwalnia obiekty nieosiągalne od korzeni (statyki, zmienne lokalne, uchwyty) — całe grafy, nie pojedyncze pola.
- Generacje 0/1/2 sprawiają, że typowa zbiórka skanuje tylko młodą część sterty; obiekty ≥ 85 000 B żyją na LOH zbieranym z Gen 2.
- Kompaktowanie usuwa fragmentację zwykłej sterty; LOH domyślnie nie jest kompaktowany.
GC.Collect()to niemal zawsze pesymalizacja — zostaw decyzję środowisku uruchomieniowemu.- „Wycieki” w C# to zapomniane referencje (statyczne kolekcje, zdarzenia) i niezamknięte zasoby —
using+ higiena subskrypcji załatwiają 90% przypadków.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
69 comments
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
Good way of telling, and good piece of writing to take facts on the topic of my presentation subject matter, which i am going to convey in university. Dacia Clemente Marva
thanks
I cannot thank you enough for the post. Really looking forward to read more. Ansley Cullie Lash
thanks
I’m truly enjoying the design and layout of your website.
It’s a very easy on the eyes which makes it much more enjoyable for me to come here and visit more often. Did you hire out a designer to create your theme?
Exceptional work!
thanks
Magnificent goods from you, man. I’ve bear in mind your
stuff previous to and you are simply extremely fantastic.
I really like what you’ve acquired here, really like what you’re saying and the best way wherein you are saying
it. You are making it enjoyable and you still care for to keep it sensible.
I can’t wait to read much more from you. That is actually a tremendous website.
thanks
I really like it when folks come together and share thoughts. Pansy Kennett Glaser
thanks
I do not even know how I finished up here, but I thought this post was
great. I don’t recognize who you are but definitely you’re going to a famous
blogger in the event you are not already. Cheers!
thanks
Hi, just wanted to say, I liked this article. It was funny. Karee Saul Far
thanks
Good response in return of this matter with firm arguments and describing everything on the topic of that. Maure Mead Carleton
thanks
You made some really good points there. I checked on the net to find out more about the issue and found most people will go along with your views on this website. Tansy Dev Roy
thanks
I was very pleased to discover this great site. I need to to thank you for ones time for this wonderful read!! I definitely appreciated every little bit of it and i also have you saved to fav to look at new things in your site. Eunice Ken Mosra
thanks
Greetings! Very helpful advice within this post! It is the little changes that will make the most important changes. Thanks a lot for sharing! Miriam Garvin Marleah
thanks
Have you ever thought about including a little bit more than just your articles? Rora Auberon Misty
thanks
Having read this I thought it was extremely enlightening. I appreciate you taking the time and energy to put this informative article together. I once again find myself spending a lot of time both reading and posting comments. But so what, it was still worth it. Ketti Jeramie Shriver
thanks
Hello there. I found your blog by way of Google at the same time as searching for a related topic, your site came up. It appears to be great. I have bookmarked it in my google bookmarks to visit then. Paola Duky Oringa
thanks
Hey there, I think your website might be having browser compatibility issues. When I look at your blog in Chrome, it looks fine but when opening in Internet Explorer, it has some overlapping. I just wanted to give you a quick heads up! Other then that, excellent blog! Clarabelle Morgen Edric
thanks
Good post. I will be facing some of these issues as well.. Pansy Tedman Hephzipah
thanks
You made some fine points there. I did a search on the subject and found most folks will go along with with your blog. Martina Rutledge Herrle
thanks
Aw, this was an exceptionally nice post. Finding the time and actual effort to make a top notch article? Dorolice Sherm Dorkus
thanks
I am actually grateful to the owner of this web site who has shared this enormous article at at this place. Arabela Rodrick Pablo
thanks
Pretty! This has been an incredibly wonderful article. Thank you for supplying this info. Vilhelmina Lockwood Durwyn
thanks
Thank you for each of your hard work on this web page. My mom takes pleasure in participating in research and it is easy to see why. I know all about the lively way you convey valuable items on the web blog and as well as cause contribution from some other people on that issue then our favorite child is really understanding a great deal. Enjoy the remaining portion of the year. Your conducting a tremendous job. Lyndsie Kerk Vladi
thanks
I really wanted to write down a small note to express gratitude to you for these unique solutions you are placing on this website. My incredibly long internet look up has at the end been honored with brilliant information to write about with my companions. I would tell you that most of us visitors actually are undeniably blessed to be in a perfect place with many outstanding individuals with interesting pointers. I feel truly lucky to have encountered your entire weblog and look forward to some more thrilling minutes reading here. Thanks a lot again for all the details. Antonina Sax Alli
thanks
Jeśli ktoś życzy sobie opinii eksperta na temat blogowania, sugeruję, aby odwiedziła ten blog internetowy.
dzieki
I’m gone to convey my little brother, that he should also visit this web site on regular
basis to obtain updated from newest news.
Please bring the continuation of the nice posts, I will be glad if you approve the comment.
Please bring the continuation of the nice posts, I will be glad if you approve the comment.
thank you admin
bardzo dobrze się czyta , bardzo praktyczny materiał
Thank you for this nice article!!!!!!
Hello, thank you for this article
I am a strict follower of you, too, can you please approve this post.
Od czasu do czasu śledzę twoją fajną witrynę dobra robota
Twój artykuł jest jak mapa dla osób, które chcą zgłębić się w [temat]. Bardzo pomocne
Wspaniały artykuł! Jesteś prawdziwym ekspertem w temacie
Nice blog, nice articles! Add favorite thanks.
Nice blog, very nice articles! Add favorite thanks.
Trzeba mieć wytrwałość i wiarę we własne siły Twój portal jest tego przykładem. Trzeba wierzyć, że człowiek jest do czegos zdolny i osiągnać to za wszelka cenę!.
Hello! good job keep it up
Niice post. I learn something totally new annd challenging on websites I stumbleupon everyday.
It’s always exciting to read through content from other writers and
practice something from their websites.
Thank you very much
Hi, i think that i saw you visited my weblog thus i came to ?return the favor?.I’m trying to find things to enhance my site!I suppose its ok to use a few of your ideas!!
Twój sposób prezentacji tematu sprawił, że nawet trudne koncepcje stały się łatwe do zrozumienia
Zawsze się czegoś nowego dowiaduję, czytając Twoje artykuły. Niezależnie od tego, czy jestem na początkowym etapie, czy już mam doświadczenie, zawsze znajduję coś wartościowego. Dzięki za to
Ciekawe spojrzenie na temat. Myślę, że warto podjąć dyskusję na ten temat w społeczności
Dzięki za dzielenie się swoją wiedzą. To naprawdę cenione
Cieszę się, że natknąłem się na ten artykuł. Precyzyjne i zwięzłe wyjaśnienia.