The Tale of Two (.NET) Heaps

4. 11. 2011 13:46 zboj

Každý programátor pracující v .NET (resp. každý dobrý, ty podprůměrné snad ani nenazývejme programátory) ví, že .NET používá dvě řízené haldy (a jednu neřízenou), jednu pro malé objekty (SOH) a jednu pro objekty větší než malé (LOH). SOH se dále dělí podle generací.

Zastánci nedeterministického GC slepě tvrdí, že .NET nemá problém s fragmentací paměti a že alokace objektů je levná (rozuměj deterministická). Nic není dále od pravdy. LOH sice nepotřebné objekty automaticky uvolňuje, ale nepřesouvá. Alokace probíhá nedeterministicky, v podstatě stejně jako např. v C++, tj. procházením seznamu volných bloků, dokud se nenajde nějaký s dostatečnou velikostí.

Dřívější verze .NET LOH neměly, Microsoft LOH vytvořil v reakci na problémy s přesouváním při GC. Důsledkem je, že v extrémních případech dostanete milou OutOfMemoryException už po alokaci objektů o celkové velikosti sotva 30 MB, i když máte v počítači 2 GB paměti. GC pro 64-bitové aplikace (tento kolektor je zcela jiný, napsaný jiným vývojářským týmem v rámci MS) se chová poněkud méně nenažraně, ale dříve nebo později skončí pro změnu swapováním a výkon jde neřízeně dolů.

Alokace velkých objektů se vyskytuje poměrně běžně, aniž by to muselo být na první pohled patrné. Např. rozhraní k databázi (ať už SQL nebo jiné) pracující s BLOBy takové objekty vytváří. Tomu se chytré databáze vyhýbají použitím proxy, ale není to pravidlem. Dalším reálným příkladem je větší pole (větší znamená v tomto případě cca. 10000 prvků, což je vlastně poměrně málo). Samotné objekty v poli jsou sice často malé (a tedy na SOH), ale samotný objekt pole, má-li prvků hodně, skončí na LOH a problém je na světě. Microsoft v těchto případech radí alokovat pole polí, pro 10000 prvků bychom tedy neměli jedno pole, ale deset polí po tisíci prvcích, načež by vše bylo v SOH a problémů s fragmentací bychom se vyhnuli.

Microsoft si je těchto problémů dobře vědom a v .NET 4.5 už je částečně vyřešil. Přesto ale zůstává faktem, že při použití C++ (ať už s /clr nebo /clr:pure) jsou problémy s fragmentací stejné jako na řízené haldě: malé objekty s ní problém nemají (vznikají buď na zásobníku nebo na SOH), ty velké se nikdy nepřesouvají. V C++ navíc existuje v STL možnost použít lepší alokátor, který problémy s fragmentací nemá.

Sdílet