C # yığın boyutu neden tam olarak 1 MB?


102

Bugünün bilgisayarlarında büyük miktarda fiziksel RAM var, ancak yine de C # yığın boyutu 32 bit işlemler için yalnızca 1 MB ve 64 bit işlemler için 4 MB'dir ( C # 'de yığın kapasitesi ).

CLR'deki yığın boyutu neden hala bu kadar sınırlı?

Ve neden tam olarak 1 MB (4 MB) (2 MB veya 512 KB değil)? Bu miktarların kullanılmasına neden karar verildi?

Bu kararın arkasındaki düşünceler ve nedenlerle ilgileniyorum .


6
64 bit işlemler için varsayılan yığın boyutu 4 MB, 32 bit işlemler için 1 MB'dir. Bu olabilir PE başlığındaki değerini değiştirerek ana lifler yığını boyutunu değiştirmek. Yapıcının doğru aşırı yüklemesini kullanarak yığın boyutunu da belirtebilirsiniz Thread. AMA, bu soruyu akla getiriyor, neden daha büyük bir yığına ihtiyacınız var?
Yuval Itzchakov

2
Teşekkürler, düzenlendi. :) Soru daha büyük yığın boyutunun nasıl kullanılacağı değil, neden yığın boyutunun 1 MB (4 MB) olduğuna karar verildiği ile ilgili .
Nikolay Kostov

8
Çünkü her iş parçacığı varsayılan olarak bu yığın boyutunu alacaktır ve çoğu iş parçacığı bu kadar fazlasına ihtiyaç duymaz. Bilgisayarımı yeni başlattım ve sistem şu anda 1200 iş parçacığı çalıştırıyor. Şimdi hesaplayın;)
Lucas Trzesniewski

2
@LucasTrzesniewski Sadece bu değil , hafızada bulaşıcı olmalı . Yığının boyutu ne kadar büyük olursa, işleminizin sanal adres alanında o kadar az iş parçacığı oluşturabileceğini unutmayın.
Yuval Itzchakov

"Tam" 1 MB olduğundan emin değilim: Windows 8.1'imde, bir .NET Core 3.1 konsol uygulamasının 1572864bayt varsayılan yığın boyutu var (GetCurrentThreadStackLimits Win32 API kullanılarak alındı). Ben açabiliyorum stackallocyaklaşık 1500000StackOverflowException olmadan bayt.
George Chakhidze

Yanıtlar:


210

görüntü açıklamasını buraya girin

Bu seçimi yapan adama bakıyorsun. David Cutler ve ekibi, varsayılan yığın boyutu olarak bir megabayt seçti. .NET veya C # ile hiçbir ilgisi yok, bu, Windows NT'yi oluşturduklarında çözüldü. Bir megabayt, bir programın EXE başlığı veya CreateThread () winapi çağrısı yığın boyutunu açıkça belirtmediğinde seçtiği şeydir. Normal yöntem olan, hemen hemen her programcı boyutu seçmesi için işletim sistemini bırakır.

Bu seçim muhtemelen Windows NT tasarımının öncesine dayanıyor, tarih bu konuda çok belirsiz. Cutler bunun hakkında bir kitap yazsaydı iyi olurdu ama o asla yazar olmadı. Bilgisayarların çalışma şekli üzerinde olağanüstü derecede etkili oldu. İlk işletim sistemi tasarımı, DEC bilgisayarlar için 16 bitlik bir işletim sistemi olan RSX-11M idi (Digital Equipment Corporation). 8-bit mikroişlemciler için ilk iyi işletim sistemi olan Gary Kildall'ın CP / M'sini büyük ölçüde etkiledi. MS-DOS'u büyük ölçüde etkiledi.

Bir sonraki tasarımı, sanal bellek destekli 32 bit işlemciler için bir işletim sistemi olan VMS idi. Çok başarılı. Bir sonraki program, şirketin ucuz PC donanımıyla rekabet edemediği sırada parçalanmaya başladığı sırada DEC tarafından iptal edildi. Cue Microsoft, ona reddedemeyeceği bir teklifte bulundular. İş arkadaşlarının çoğu da katıldı. Daha çok Windows NT olarak bilinen VMS v2 üzerinde çalıştılar. DEC buna üzüldü, halletmek için para el değiştirdi. VMS'nin zaten bir megabayt seçip seçmediğini bilmiyorum, sadece RSX-11'i yeterince iyi biliyorum. Olası değil.

Yeterince tarih. Bir megabayt çoktur , gerçek bir iş parçacığı nadiren birkaç avuç kilobayttan fazla tüketir. Yani bir megabayt aslında oldukça savurgan. Bununla birlikte, talep sayfalı bir sanal bellek işletim sisteminde karşılayabileceğiniz türden bir israftır, bu megabayt yalnızca sanal bellektir . İşlemciye sadece sayılar, her 4096 bayt için birer tane. Makinede bulunan fiziksel belleği, RAM'i, gerçekten adreslenene kadar asla kullanmazsınız.

Bir .NET programında fazladan fazladır çünkü bir megabayt boyutu orijinal olarak yerel programları barındıracak şekilde seçilmiştir. Büyük yığın çerçeveleri oluşturma eğiliminde olan, yığın üzerinde dizeleri ve arabellekleri (dizileri) depolayan. Kötü amaçlı yazılım saldırı vektörü olduğu için kötü bir şöhrete sahip olan bir arabellek taşması, programı verilerle değiştirebilir. .NET programlarının çalışma şekli değil, dizeler ve diziler GC yığınına ayrılır ve indeksleme kontrol edilir. Yığın üzerinde C # ile alan ayırmanın tek yolu, güvenli olmayan stackalloc'dur. anahtar sözcüğünü kullanmaktır.

.NET'te yığının önemsiz tek kullanımı jitterdir. İş parçacığınızın yığınını, MSIL'i tam zamanında derlemek için kod makinesine kullanır. Ne kadar alan gerektirdiğini hiç görmedim veya kontrol etmedim, daha çok kodun doğasına ve optimize edicinin etkin olup olmadığına bağlıdır, ancak birkaç on kilobayt kaba bir tahmindir. Aksi takdirde, bu web sitesi adını nasıl almıştır, bir .NET programındaki yığın taşması oldukça ölümcüldür. İstisnayı yakalamaya çalışan herhangi bir kodu hala güvenilir bir şekilde JITlemek için yeterli alan (3 kilobayttan az) kalmadı. Masaüstüne Kaboom tek seçenektir.

Son olarak, bir .NET programı yığınla oldukça verimsiz bir şey yapar. CLR , bir iş parçacığının yığınını işleyecektir . Bu pahalı bir kelimedir, yani sadece yığının boyutunu rezerve etmekle kalmaz, aynı zamanda işletim sisteminin sayfalama dosyasında yer ayrılmasını sağlar, böylece yığın gerektiğinde her zaman değiştirilebilir. Tamamlanamama ölümcül bir hatadır ve bir programı koşulsuz olarak sonlandırır. Bu, yalnızca tamamen çok fazla işlem çalıştıran çok az RAM'e sahip bir makinede gerçekleşir, böyle bir makine programlar ölmeye başlamadan önce pekmeze dönmüş olacaktır. 15+ yıl önce olası bir sorun, bugün değil. Programlarını bir F1 yarış arabası gibi davranacak şekilde ayarlayan programcılar, <disableCommitThreadStack>.config dosyalarındaki öğeyi kullanır .

Fwiw, Cutler işletim sistemlerini tasarlamayı bırakmadı. Bu fotoğraf Azure üzerinde çalışırken çekilmiş.


Güncelleme, .NET'in artık yığını işlemediğini fark ettim. Bunun ne zaman ve neden olduğundan tam olarak emin değilim, kontrol etmeyeli çok uzun zaman oldu. Bu tasarım değişikliğinin .NET 4.5 civarında bir yerde olduğunu tahmin ediyorum. Oldukça mantıklı bir değişiklik.


3
yorumunuza wrt The only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.- Yerel değişkenler, örneğin bir intyöntem içinde tanımlanmış bir yığın üzerinde depolanmamış mı? Sanırım öyleler.
RBT

2
Tamam. Şimdi anladım ki, bir fonksiyonun yerel değişkenleri için bir yığın çerçevesi tek depolama seçeneği değildir. Bu edilebilir senin mermi noktalarından birinde söylediği gibi olsa yığın çerçeve üzerinde depolanan. Çok aydınlatıcı Hans. Bu kadar anlayışlı yazılar yazdığınız için ne kadar teşekkür ederim diyemem. Dürüst olmak gerekirse yığın, sadece gereksiz karmaşıklığı önlemek için genel olarak programlama için çok büyük bir soyutlamadır.
RBT

Çok ayrıntılı açıklama @Hans. maxStackSizeBir iş parçacığının minimum olası değeri nedir? [MSDN] üzerinde bulamadım ( msdn.microsoft.com/en-us/library/5cykbwz4(v=vs.110).aspx ). Yorumlarınıza göre yığın kullanımı minimum düzeyde görünüyor ve mümkün olan maksimum iş parçacığını barındırmak için en küçük değeri kullanabilirim. Teşekkürler.
MKR

1
@KFL: Sorunuzu deneyerek kolayca cevaplayabilirsiniz!
Eric Lippert

1
Varsayılan davranış artık yığını işlemeyecekse, bu işaretleme dosyasının düzenlenmesi gerekir github.com/dotnet/docs/blob/master/docs/framework/…
John Stewien

5

Varsayılan ayrılmış yığın boyutu, bağlayıcı tarafından belirlenir ve geliştiriciler tarafından bağlantı zamanında PE değerini değiştirerek veya WinAPI işlevi için dwStackSizeparametre belirterek tek bir iş parçacığı için geçersiz kılınabilir CreateThread.

İlk yığın boyutu varsayılan yığın boyutundan daha büyük veya ona eşit olan bir iş parçacığı oluşturursanız, en yakın 1 MB katına yuvarlanır.

Değer neden 32 bit işlemler için 1 MB ve 64 bit için 4 MB'ye eşittir? Sanırım Windows'u tasarlayan geliştiricilere sormalı veya onlardan biri sorunuzu yanıtlayana kadar beklemelisiniz.

Muhtemelen Mark Russinovich bunu biliyor ve onunla iletişime geçebilirsiniz . Belki bu bilgiyi, makalesi yerine yığınlar hakkında daha az bilgi açıklayan altıncı baskıdan önceki Windows Internals kitaplarında bulabilirsiniz . Ya da belki Raymond Chen, Windows iç bileşenleri ve tarihi hakkında ilginç şeyler yazdığı için nedenleri biliyordur. O da sorunuza cevap verebilir, ancak Öneri Kutusuna bir öneri göndermelisiniz .

Ancak şu anda, Microsoft'un bu değerleri seçmesinin olası bazı nedenlerini MSDN, Mark's ve Raymond'un bloglarını kullanarak açıklamaya çalışacağım.

Varsayılanlar bu değerlere sahip olabilir, çünkü erken dönemlerde PC'ler yavaştı ve yığın üzerinde bellek ayırmak, bellek yığınına ayırmaktan çok daha hızlıydı. Yığın ayırmaları çok daha ucuz olduğu için kullanıldılar, ancak daha büyük yığın boyutu gerektiriyordu.

Dolayısıyla değer, çoğu uygulama için en uygun ayrılmış yığın boyutuydu. Optimaldir çünkü çok sayıda iç içe geçmiş çağrı yapmaya ve yapıları çağrı işlevlerine geçirmek için yığın üzerinde bellek ayırmaya izin verir. Aynı zamanda çok sayıda iplik oluşturmaya izin verir.

Günümüzde bu değerler çoğunlukla geriye dönük uyumluluk için kullanılmaktadır, çünkü WinAPI işlevlerine parametre olarak aktarılan yapılar hala yığın üzerinde tahsis edilmektedir. Ancak yığın ayırmalarını kullanmıyorsanız, iş parçacığının yığın kullanımı varsayılan 1 MB'den önemli ölçüde daha az olacaktır ve Hans Passant'ın bahsettiği gibi israf olur. Ve bunu önlemek için işletim sistemi, uygulamanın PE başlığında başka bir sayfa belirtilmemişse yığının yalnızca ilk sayfasını (4 KB) işler. Diğer sayfalar talep üzerine tahsis edilir.

Bazı uygulamalar ayrılmış adres alanını geçersiz kılar ve başlangıçta bellek kullanımını optimize etmeyi taahhüt eder. Örnek olarak, bir IIS yerel işleminin iş parçacığının maksimum yığın boyutu 256 KB'dir ( KB932909 ). Ve varsayılan değerlerin bu şekilde azaltılması Microsoft tarafından önerilir :

Mümkün olduğunca küçük bir yığın boyutu seçmek ve iplik veya elyafın güvenilir bir şekilde çalışması için gereken yığını yürütmek en iyisidir. Yığın için ayrılan her sayfa başka bir amaçla kullanılamaz.

Kaynaklar:

  1. İş Parçacığı Yığın Boyutu (Microsoft Docs)
  2. Windows'un Sınırlarını Zorlamak: İşlemler ve İş Parçacıkları (Mark Russinovich)
  3. Varsayılan olarak, yerel bir IIS işleminde oluşturulan bir iş parçacığının maksimum yığın boyutu 256 KB'dir (KB932909)

Daha büyük bir yığın boyutu istersem, onu ayarlayabilirim ( atalasoft.com/cs/blogs/rickm/archive/2008/04/22/… ). Bu kararın arkasındaki nedenleri ve nedenleri bilmek istiyorum.
Nikolay Kostov

2
Tamam. Şimdi sizi anlıyorum :) Varsayılan yığın boyutu en uygun olmalıdır (bkz. @Lucas Trzesniewski yorumu) ve ayırma granülerliğinin en yakın katına yuvarlanmalıdır. Belirtilen yığın boyutu varsayılan yığın boyutundan büyükse, 1MB'nin en yakın katına yuvarlanır. Bu nedenle Microsoft, bu boyutları tüm kullanıcı modu uygulamaları için varsayılan yığın boyutları olarak seçti. Ve başka sebep yok.
Yoh Deadfall

Kaynak var mı? Herhangi bir belge var mı? :)
Nikolay Kostov

@Yoh ilginç bağlantı. Bunu cevabınızda özetlemelisiniz.
Lucas Trzesniewski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.