Aracılığıyla paylaş


Windows sistemlerindeki büyük nesne yığını

.NET çöp toplayıcısı (GC), nesneleri küçük ve büyük nesnelere böler. Bir nesne büyük olduğunda, bazı öznitelikleri nesnenin küçük olduğundan daha önemli hale gelir. Örneğin sıkıştırmak( başka bir deyişle yığının başka bir yerine bellekte kopyalamak) pahalıya patlayabilir. Bu nedenle, çöp toplayıcı büyük nesne yığınına (LOH) büyük nesneler yerleştirir. Bu makalede, bir nesneyi büyük bir nesne olarak nitelemeyi, büyük nesnelerin nasıl toplandığını ve büyük nesnelerin ne tür performans etkilerine neden olduğu açıklanır.

Önemli

Bu makalede, yalnızca Windows sistemlerinde çalışan .NET Framework ve .NET Core'daki büyük nesne yığını ele alınmaktadır. Diğer platformlarda .NET uygulamalarında çalışan LOH'yi kapsamaz.

Bir nesne nasıl LOH'ta sona erer?

Nesne boyutu 85.000 bayttan büyük veya buna eşitse, büyük bir nesne olarak kabul edilir. Bu sayı, performans ayarlamaları ile belirlendi. Bir nesne ayırma isteği 85.000 veya daha fazla bayt için olduğunda, çalışma zamanı bunu büyük nesne yığınına ayırır.

Bunun ne anlama geldiğini anlamak için, çöp toplayıcı hakkında bazı temelleri incelemek yararlı olur.

Çöp toplayıcı, nesil temelli bir toplayıcıdır. Üç nesil vardır: 0. nesil, 1. nesil ve 2. nesil. Üç nesil olmasının nedeni, iyi ayarlanmış bir uygulamada çoğu nesnenin 0. nesilde ölmesidir. Örneğin, bir sunucu uygulamasında, her istekle ilişkili ayırmaların, istek tamamlandıktan sonra sonlanması gerekir. Uçuş içi ayırma istekleri onu 1. nesile dönüştürecek ve orada ölecek. Temelde, 1. nesil, genç nesne alanları ve uzun ömürlü nesne alanları arasında bir arabellek işlevi görür.

Yeni ayrılan nesneler yeni bir nesne nesli oluşturur ve örtük olarak 0. nesil koleksiyonlardır. Ancak, büyük nesnelerse, bazen 3. nesil olarak adlandırılan büyük nesne yığınına (LOH) gider. 3. nesil, mantıksal olarak 2. neslin bir parçası olarak toplanan fiziksel bir nesildir.

Büyük nesneler yalnızca 2. nesil bir koleksiyon sırasında toplandığından 2. nesil nesnelere aittir. Bir nesil toplandığında, tüm daha genç kuşak(lar) da toplanır. Örneğin, 1. nesil GC gerçekleştiğinde hem 1. nesil hem de 0 toplanır. 2. nesil GC gerçekleştiğinde de yığının tamamı toplanır. Bu nedenle, 2. nesil GC tam GC olarak da adlandırılır. Bu makale, tam GC yerine 2. nesil GC'yi ifade eder, ancak terimler birbirinin yerine değiştirilebilir.

Nesiller GC yığınının mantıksal bir görünümünü sağlar. Fiziksel olarak, nesneler yönetilen yığın segmentlerinde yaşar. Yönetilen yığın kesimi, yönetilen kod adına VirtualAlloc işlevini çağırarak GC'nin işletim sisteminden ayırdığı bir bellek öbeğidir. CLR yüklendiğinde GC iki ilk yığın kesimi ayırır: biri küçük nesneler (küçük nesne yığını veya SOH) ve biri büyük nesneler (büyük nesne yığını) için.

Daha sonra bu yönetilen yığın segmentlerine yönetilen nesneler yerleştirilerek ayırma istekleri karşılanır. Nesne 85.000 bayttan küçükse, SOH segmentine yerleştirilir; aksi takdirde, bir LOH segmentine yerleştirilir. Segmentler, daha fazla nesne tahsis edilince (daha küçük parçalar halinde) ayrılır. SOH için, çöp toplayıcıdan (GC) kurtulan nesneler bir sonraki nesile terfi ettirilir. 0. nesil bir koleksiyondan kurtulan nesneler artık 1. nesil nesneler olarak kabul edilir ve bu şekilde devam eder. Ancak, en eski nesilden hayatta kalan nesneler hala en eski nesilde olarak kabul edilir. Başka bir deyişle, 2. nesilden sağ kalanlar 2. nesil nesnelerdir; ve LOH'dan kurtulanlar LOH nesneleridir (gen2 ile toplanır).

Kullanıcı kodu yalnızca 0. nesilde (küçük nesneler) veya LOH'da (büyük nesneler) tahsis yapabilir. Yalnızca GC, 1. nesildeki objeleri (0. nesilden hayatta kalanları yükselterek) ve 2. nesildeki objeleri (1. nesilden hayatta kalanları terfi ettirerek) "ayırabilir".

Bir çöp toplama tetiklendiğinde, GC canlı nesneleri izler ve bunları sıkıştırır. Ancak sıkıştırma pahalı olduğundan GC, LOH'yi süpürür ; daha sonra büyük nesne ayırma isteklerini karşılamak için yeniden kullanılabilecek boş nesnelerin listesini oluşturur. Bitişik ölü nesneler tek bir serbest nesneye dönüştürülür.

.NET Core ve .NET Framework (.NET Framework 4.5.1'den itibaren), kullanıcıların bir sonraki tam blokajlı GC sırasında LOH'un sıkıştırılması gerektiğini belirlemesine olanak tanıyan GCSettings.LargeObjectHeapCompactionMode özelliğini içerir. Gelecekte .NET, LOH'yi otomatik olarak sıkıştırmaya karar verebilir. Başka bir deyişle, büyük nesneleri ayırır ve taşınmadığından emin olmak isterseniz, yine de bunları sabitlemeniz gerekir.

Şekil 1, ilk nesil 0 GC'nin ardından, Obj1 ve Obj3 öldükten sonra GC'nin 1. nesli oluşturduğu senaryoyu ve ilk nesil 1 GC'nin ardından Obj2 ve Obj5 öldükten sonra 2. nesli oluşturduğu senaryoyu göstermektedir. Bunun ve aşağıdaki rakamların yalnızca çizim amaçlı olduğunu unutmayın; yığında ne olduğunu daha iyi göstermek için çok az nesne içerir. Gerçekte, bir GC'ye genellikle daha fazla nesne dahil edilir.

Şekil 1: 0. nesil GC ve 1. nesil GC
Şekil 1: 0. nesil ve 1. nesil GC.

Şekil 2, 2. nesil bir GC'nin Obj1 ve Obj2 öldükten sonra Obj1 ve Obj2 tarafından eskiden kullanılan bellekte bitişik boş alan oluşturduğunu ve bu alanın daha sonra Obj4 için bir ayırma isteğini karşılamada kullanıldığını göstermektedir. Ayırma isteklerini karşılamak için, segmentin sonuna kadar olan son nesneden Obj3sonraki alan da kullanılabilir.

Şekil 2: 2. Nesil GC'nin ardından
Şekil 2: 2. nesil GC'nin ardından

Büyük nesne ayırma isteklerini karşılamak için yeterli boş alan yoksa GC ilk olarak işletim sisteminden daha fazla segment almaya çalışır. Bu başarısız olursa, biraz alan boşaltma umuduyla 2. nesil GC'yi tetikler.

1. nesil veya 2. nesil GC sırasında çöp toplayıcı, üzerinde canlı nesne olmayan kesimleri VirtualFree işlevini çağırarak işletim sistemine geri gönderir. Segmentin sonuna kadar olan son canlı nesneden sonraki alan kullanım dışı bırakılır (Gen0/Gen1'in canlı olduğu kısa ömürlü segment dışında; bu segmentte çöp toplayıcı, uygulamanızın hemen kaynak tahsis edeceği alanı tutmaya devam eder). Boş alanlar sıfırlandıklarına rağmen işlenmeye devam eder. Bu da işletim sisteminin diske geri veri yazması gerekmemesi anlamına gelir.

LOH yalnızca 2. nesil GC'ler sırasında toplandığından, LOH segmenti yalnızca böyle bir GC sırasında serbest bırakılır. Şekil 3'te, çöp toplayıcının bir segmenti (segment 2) işletim sistemine geri bıraktığı ve kalan segmentlerde daha fazla alan bıraktığı bir senaryo gösterilmektedir. Büyük nesne ayırma isteklerini karşılamak için bölümün sonundaki serbest bırakılan alanı kullanması gerekiyorsa, belleği yeniden tahsis eder. (commit/decommit açıklaması için VirtualAlloc belgelerine bakın.)

Şekil 3: 2. nesil GC'nin ardından LOH
Şekil 3: 2. nesil GC'nin ardından gelen LOH

Büyük bir nesne ne zaman toplanır?

Genel olarak, GC aşağıdaki üç koşuldan biri sırasında gerçekleşir:

  • Ayırma, 0. nesil veya büyük nesne eşiklerini aşıyor.

    Eşik, bir neslin özelliğidir. Çöp toplayıcı, nesneleri bir nesile ayırdığında o nesil için bir eşik belirlenir. Eşik aşıldığında, ilgili nesilde bir GC tetiklenir. Küçük veya büyük nesneleri ayırdığınızda, sırasıyla 0. nesil ve LOH eşiklerini kullanırsınız. Atık toplayıcı 1. ve 2. nesillere ayrıldığında eşiklerini tüketir. Bu eşikler, program çalışırken dinamik olarak ayarlanmıştır.

    Bu tipik bir durumdur; çoğu GC yönetilen yığındaki ayırmalar nedeniyle oluşur.

  • GC.Collect yöntemi çağrılır.

    Parametresiz GC.Collect() yöntemi çağrılırsa veya başka bir aşırı yükleme bağımsız değişken olarak GC.MaxGeneration geçirilirse, LOH yönetilen yığının geri kalanıyla birlikte toplanır.

  • Sistem düşük bellek durumunda.

    Bu durum, atık toplayıcı işletim sisteminden yüksek bellek bildirimi aldığında oluşur. Çöp toplayıcı, 2. nesil GC yapmanın üretken olacağını düşünüyorsa, bir tane tetikler.

LOH performansının etkileri

Büyük nesne yığınındaki ayırmalar performansı aşağıdaki yollarla etkiler.

  • Tahsis maliyeti.

    CLR, verdiği her yeni nesnenin belleğinin temizlenmesini garanti eder. Bu, büyük bir nesnenin ayırma maliyetinin bellek temizleme (GC tetiklemediği sürece) tarafından baskın olduğu anlamına gelir. Bir bayt temizlemek için iki döngü gerekiyorsa, en küçük büyük nesneyi temizlemek için 170.000 döngü gerekir. 2 GHz makinedeki 16 MB'lık nesnenin belleğinin temizlenmesi yaklaşık 16 ms sürer. Bu oldukça büyük bir maliyet.

  • Koleksiyon maliyeti.

    LOH ve 2. nesil birlikte toplandığından, birinin eşiği aşılırsa 2. nesil bir koleksiyon tetiklenir. 2. nesil bir koleksiyon LOH nedeniyle tetikleniyorsa, 2. nesil GC'nin ardından çok daha küçük olmayabilir. 2. nesilde çok fazla veri yoksa bunun çok az etkisi vardır. Ancak 2. nesil büyükse, çok sayıda 2. nesil GC tetiklendiğinde performans sorunlarına neden olabilir. Birçok büyük nesne geçici olarak ayrılmışsa ve büyük bir SOH'niz varsa, GC'leri yaparken çok fazla zaman harcıyor olabilirsiniz. Buna ek olarak, çok büyük nesneleri tahsis etmeye ve serbest bırakmaya devam ederseniz, tahsis maliyeti önemli ölçüde artabilir.

  • Referans türlerine sahip dizi öğeleri.

    LOH üzerindeki çok büyük nesneler genellikle dizilerdir (gerçekten büyük bir örnek nesnesi olması çok nadirdir). Bir dizinin öğeleri referans açısından yoğun olduğunda, öğeler referans açısından yoğun olmadığında oluşmayan bir maliyete neden olur. Eğer öğe hiçbir referans içermiyorsa, çöp toplayıcının diziyi hiçbir şekilde işleme gerekmez. Örneğin, bir ikili ağacın düğümlerini depolamak için bir dizi kullanırsanız, bunu uygulamanın bir yolu, bir düğümün sağ ve sol düğümüne gerçek düğümlere başvurarak ulaşmaktır.

    class Node
    {
       Data d;
       Node left;
       Node right;
    };
    
    Node[] binary_tr = new Node [num_nodes];
    

    Eğer num_nodes büyükse, çöp toplayıcının her öğe için en az iki referanstan geçmesi gerekir. Alternatif bir yaklaşım, sağ ve sol düğümlerin dizinini depolamaktır:

    class Node
    {
       Data d;
       uint left_index;
       uint right_index;
    } ;
    

    Sol düğümün verilerine left.d yerine binary_tr[left_index].d olarak başvuruyorsunuz. Ve çöp toplayıcının sol ve sağ düğüm için referanslara bakması gerekmez.

Üç faktörden ilk ikisi genellikle üçüncü faktörden daha önemlidir. Bu nedenle, geçici nesneler ayırmak yerine yeniden kullandığınız büyük nesnelerden oluşan bir havuz ayırmanızı öneririz.

LOH için performans verilerini toplama

Belirli bir alan için performans verilerini toplamadan önce, aşağıdakileri zaten yapmış olmanız gerekir:

  1. Bu alana bakman gerektiğine dair kanıt buldum.
  2. Gördüğünüz performans sorununu açıklayacak hiçbir şey bulamadan bildiğiniz diğer alanları tükettiniz.

Belleğin ve CPU'nun temelleri hakkında daha fazla bilgi için bir çözüm bulmaya çalışmadan önce sorunu anlama blogunu inceleyin.

LOH performansıyla ilgili verileri toplamak için aşağıdaki araçları kullanabilirsiniz:

.NET CLR Bellek performans sayaçları

.NET CLR Bellek performans sayaçları genellikle performans sorunlarını araştırmada iyi bir ilk adımdır ( ETW olaylarını kullanmanızı öneririz). Performans sayaçlarına bakmanın yaygın yollarından biri Performans İzleyicisi 'dir (perfmon.exe). İlgilendiğiniz işlemlere yönelik ilginç sayaçları eklemek için Ekle (Ctrl + A) öğesini seçin. Performans sayacı verilerini bir günlük dosyasına kaydedebilirsiniz.

.NET CLR Bellek kategorisindeki aşağıdaki iki sayaç LOH için geçerlidir:

  • # 2. Nesil Koleksiyonlar

    İşlem başladıktan sonra 2. nesil GC'lerin oluşma sayısını görüntüler. Sayaç, 2. nesil toplama (tam çöp toplanması olarak da adlandırılır) sonunda artırılır. Bu sayaç, gözlemlenen son değeri görüntüler.

  • Büyük Nesne Yığını boyutu

    LOH'nin boş alan da dahil olmak üzere geçerli boyutunu bayt cinsinden görüntüler. Bu sayaç, her ayırmada değil, çöp toplamanın sonunda güncelleştirilir.

Performans İzleyicisi'nde sayaç eklemeyi gösteren ekran görüntüsü.

Ayrıca sınıfını kullanarak PerformanceCounter program aracılığıyla performans sayaçlarını sorgulayabilirsiniz. LOH için ".NET CLR Bellek" ve "Büyük Nesne Yığını boyutu" olarak CategoryName ve CounterName belirtin.

PerformanceCounter performanceCounter = new()
{
    CategoryName = ".NET CLR Memory",
    CounterName = "Large Object Heap size",
    InstanceName = "<instance_name>"
};

Console.WriteLine(performanceCounter.NextValue());

Rutin test sürecinin bir parçası olarak sayaçları program aracılığıyla toplamak yaygın bir durumdur. Normal olmayan değerlerle sayaçları tespit ettiğinizde, araştırmaya yardımcı olmak için daha ayrıntılı veriler almak için diğer araçları kullanın.

Uyarı

ETW çok daha zengin bilgiler sağladığından performans sayaçları yerine ETW olaylarını kullanmanızı öneririz.

ETW olayları

Çöp toplayıcı, yığının ne yaptığını ve nedenini anlamanıza yardımcı olmak için zengin bir ETW olayları kümesi sağlar. Aşağıdaki blog gönderileri, ETW ile GC olaylarını toplamayı ve anlamayı gösterir:

Geçici LOH ayırmalarının neden olduğu aşırı 2. nesil GC'leri tanımlamak için, GC'lerin Tetikleyici Nedeni sütununa bakın. Yalnızca geçici büyük nesneleri ayıran basit bir test için, aşağıdaki PerfView komutuyla ETW olayları hakkında bilgi toplayabilirsiniz:

perfview /GCCollectOnly /AcceptEULA /nogui collect

Sonuç şuna benzer:

PerfView'da ETW olaylarını gösteren ekran görüntüsü.

Gördüğünüz gibi, tüm GC'ler 2. nesil GC'lerdir ve tümü AllocLarge tarafından tetiklenir. Bu da büyük bir nesne ayırmanın bu GC'yi tetiklediği anlamına gelir. LOH Hayatta Kalma Oranı % sütununda 1%yazdığı için bu ayırmaların geçici olduğunu biliyoruz.

Sana bu büyük nesneleri kimin ayırdığını söyleyen ek ETW olayları toplayabilirsiniz. Aşağıdaki komut satırı:

perfview /GCOnly /AcceptEULA /nogui collect

yaklaşık her 100 bin ayırmada tetiklenen bir AllocationTick olayı toplar. Başka bir deyişle, büyük bir nesne her tahsis edildiğinde bir olay tetiklenir. Daha sonra, büyük nesneleri ayıran çağrı yığınlarını gösteren GC Heap Alloc görünümlerinden birine bakabilirsiniz:

Çöp toplayıcı yığın görünümünü gösteren ekran görüntüsü.

Gördüğünüz gibi, bu test yalnızca Main metodundan büyük nesneler ayırıyor.

Hata ayıklayıcısı

Sahip olduğunuz tek şey bir bellek dökümüyse ve LOH'da gerçekte hangi nesnelerin olduğuna bakmanız gerekiyorsa, .NET tarafından sağlanan SoS hata ayıklayıcısı uzantısını kullanabilirsiniz.

Uyarı

Bu bölümde belirtilen hata ayıklama komutları Windows hata ayıklayıcıları için geçerlidir.

Aşağıda LOH analizinden elde edilen örnek çıkış gösterilmektedir:

0:003> .loadby sos mscorwks
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x013e35ec
sdgeneration 1 starts at 0x013e1b6c
generation 2 starts at 0x013e1000
ephemeral segment allocation context: none
segment   begin allocated     size
0018f2d0 790d5588 790f4b38 0x0001f5b0(128432)
013e0000 013e1000 013e35f8 0x000025f8(9720)
Large object heap starts at 0x023e1000
segment   begin allocated     size
023e0000 023e1000 033db630 0x00ffa630(16754224)
033e0000 033e1000 043cdf98 0x00fecf98(16699288)
043e0000 043e1000 05368b58 0x00f87b58(16284504)
Total Size 0x2f90cc8(49876168)
------------------------------
GC Heap Size 0x2f90cc8(49876168)
0:003> !dumpheap -stat 023e1000 033db630
total 133 objects
Statistics:
MT   Count   TotalSize Class Name
001521d0       66     2081792     Free
7912273c       63     6663696 System.Byte[]
7912254c       4     8008736 System.Object[]
Total 133 objects

LOH yığın boyutu (16.754.224 + 16.699.288 + 16.284.504) = 49.738.016 bayttır. 023e1000 ve 033db630 adresleri arasında, 8.008.736 bayt bir nesne dizisi System.Object tarafından işgal edilir, 6.663.696 bayt bir nesne dizisi System.Byte tarafından kaplanmıştır ve 2.081.792 bayt boş alan tarafından işgal edilir.

Bazen hata ayıklayıcı, LOH'nin toplam boyutunun 85.000 bayttan az olduğunu gösterir. Bunun nedeni, çalışma zamanının büyük bir nesneden daha küçük olan bazı nesneleri ayırmak için LOH'yi kullanmasıdır.

LOH sıkıştırılmadığından, LOH'un bazen parçalanmanın kaynağı olduğu düşünülüyor. Parçalanma şu anlama gelir:

  • Yönetilen nesneler arasındaki boş alan miktarıyla gösterilen yönetilen yığının parçalanması. SoS'da !dumpheap –type Free komut, yönetilen nesneler arasındaki boş alan miktarını görüntüler.

  • olarak MEM_FREEişaretlenmiş bellek olan sanal bellek (VM) adres alanının parçalanması. Windbg'de çeşitli hata ayıklayıcı komutlarını kullanarak alabilirsiniz.

    Aşağıdaki örnekte VM alanında parçalanma gösterilmektedir:

    0:000> !address
    00000000 : 00000000 - 00010000
    Type     00000000
    Protect 00000001 PAGE_NOACCESS
    State   00010000 MEM_FREE
    Usage   RegionUsageFree
    00010000 : 00010000 - 00002000
    Type     00020000 MEM_PRIVATE
    Protect 00000004 PAGE_READWRITE
    State   00001000 MEM_COMMIT
    Usage   RegionUsageEnvironmentBlock
    00012000 : 00012000 - 0000e000
    Type     00000000
    Protect 00000001 PAGE_NOACCESS
    State   00010000 MEM_FREE
    Usage   RegionUsageFree
    … [omitted]
    -------------------- Usage SUMMARY --------------------------
    TotSize (     KB)   Pct(Tots) Pct(Busy)   Usage
    701000 (   7172) : 00.34%   20.69%   : RegionUsageIsVAD
    7de15000 ( 2062420) : 98.35%   00.00%   : RegionUsageFree
    1452000 (   20808) : 00.99%   60.02%   : RegionUsageImage
    300000 (   3072) : 00.15%   08.86%   : RegionUsageStack
    3000 (     12) : 00.00%   00.03%   : RegionUsageTeb
    381000 (   3588) : 00.17%   10.35%   : RegionUsageHeap
    0 (       0) : 00.00%   00.00%   : RegionUsagePageHeap
    1000 (       4) : 00.00%   00.01%   : RegionUsagePeb
    1000 (       4) : 00.00%   00.01%   : RegionUsageProcessParametrs
    2000 (       8) : 00.00%   00.02%   : RegionUsageEnvironmentBlock
    Tot: 7fff0000 (2097088 KB) Busy: 021db000 (34668 KB)
    
    -------------------- Type SUMMARY --------------------------
    TotSize (     KB)   Pct(Tots) Usage
    7de15000 ( 2062420) : 98.35%   : <free>
    1452000 (   20808) : 00.99%   : MEM_IMAGE
    69f000 (   6780) : 00.32%   : MEM_MAPPED
    6ea000 (   7080) : 00.34%   : MEM_PRIVATE
    
    -------------------- State SUMMARY --------------------------
    TotSize (     KB)   Pct(Tots) Usage
    1a58000 (   26976) : 01.29%   : MEM_COMMIT
    7de15000 ( 2062420) : 98.35%   : MEM_FREE
    783000 (   7692) : 00.37%   : MEM_RESERVE
    
    Largest free region: Base 01432000 - Size 707ee000 (1843128 KB)
    

Atık toplayıcının sık sık işletim sisteminden yeni yönetilen yığın kesimleri almasını ve boş olanları işletim sistemine geri bırakmasını gerektiren geçici büyük nesnelerin neden olduğu VM parçalanmalarını görmek daha yaygındır.

LOH'un VM parçalanmalarına neden olup olmadığını doğrulamak için VirtualAlloc ve VirtualFree'de bir kesme noktası ayarlayarak bunları kimin çağırdığını görebilirsiniz. Örneğin, işletim sisteminden 8 MB'tan büyük sanal bellek öbeklerini ayırmaya çalışan kişileri görmek için aşağıdaki gibi bir kesme noktası ayarlayabilirsiniz:

bp kernel32!virtualalloc "j (dwo(@esp+8)>800000) 'kb';'g'"

Bu komut hata ayıklayıcıya girer ve çağrı yığınını yalnızca VirtualAlloc 8 MB'tan (0x800000) büyük bir ayırma boyutuyla çağrıldığında gösterir.

CLR 2.0, segmentlerin (büyük ve küçük nesne yığınları dahil) sık sık alındığı ve yayımlandığı senaryolar için yararlı olabilecek VM Biriktiricisi adlı bir özellik ekledi. VM Hoarding'i belirtmek için, barındırma API'si aracılığıyla STARTUP_HOARD_GC_VM adı verilen bir başlangıç parametresi belirlersiniz. CLR, boş kesimleri işletim sistemine geri göndermek yerine bu kesimlerdeki belleğin kullanımdan kalkmasını sağlar ve bunları bekleme listesine alır. (CLR'nin bunu çok büyük segmentler için yapmadığını unutmayın.) CLR daha sonra yeni segment isteklerini karşılamak için bu kesimleri kullanır. Uygulamanızın bir sonraki yeni segmente ihtiyacı olduğunda CLR, yeterince büyük bir segment bulabiliyorsa bu bekleme listesindeki bir segmenti kullanır.

VM istifleme, sistemde çalışan baskın uygulamalar olan bazı sunucu uygulamaları gibi, önceden edindikleri kesimleri tutmak isteyen ve bellek yetersizliği özel durumlarını önlemeyi amaçlayan uygulamalar için de yararlıdır.

Uygulamanızın oldukça kararlı bellek kullanımına sahip olduğundan emin olmak için bu özelliği kullanırken uygulamanızı dikkatle test etmenizi kesinlikle öneririz.