เหตุใดขนาดสแต็กใน C # จึงเท่ากับ 1 MB


102

พีซีในปัจจุบันมี RAM จริงจำนวนมาก แต่ขนาดสแต็กของ C # มีเพียง 1 MB สำหรับกระบวนการ 32 บิตและ 4 MB สำหรับกระบวนการ 64 บิต ( ความจุกองใน C # )

ทำไมขนาดสแต็กใน CLR จึงยังมี จำกัด

แล้วทำไมถึงเป็น 1 MB (4 MB) (ไม่ใช่ 2 MB หรือ 512 KB) เหตุใดจึงตัดสินใจใช้เงินจำนวนนี้

ฉันสนใจข้อพิจารณาและเหตุผลเบื้องหลังการตัดสินใจนั้น


6
ขนาดสแต็กเริ่มต้นสำหรับกระบวนการ 64 บิตคือ 4MB เป็น 1MB สำหรับกระบวนการ 32 บิต คุณสามารถแก้ไขขนาดสแต็กเธรดหลักได้โดยการเปลี่ยนค่าในส่วนหัว PE นอกจากนี้คุณยังสามารถระบุขนาดสแต็คโดยใช้เกินขวาของThreadตัวสร้าง แต่สิ่งนี้ทำให้เกิดคำถามว่าทำไมคุณถึงต้องการกองขนาดใหญ่?
Yuval Itzchakov

2
ขอบคุณแก้ไข :) คำถามไม่ได้เกี่ยวกับวิธีการใช้สแต็คขนาดขนาดใหญ่ แต่ทำไมขนาดกองตัดสินใจที่จะเป็น 1 เมกะไบต์ (4 MB)
Nikolay Kostov

8
เนื่องจากแต่ละเธรดจะได้รับขนาดสแต็กตามค่าเริ่มต้นและเธรดส่วนใหญ่ไม่ต้องการขนาดนั้น ฉันเพิ่งบูทพีซีของฉันและระบบกำลังรัน 1200 เธรด ตอนนี้ทำคณิตศาสตร์;)
Lucas Trzesniewski

2
@LucasTrzesniewski ไม่เพียง แต่ที่มันจะต้องเป็นโรคติดต่อในหน่วยความจำ สังเกตขนาดของสแต็กที่ใหญ่ขึ้นเธรดที่กระบวนการของคุณสามารถสร้างได้น้อยลงในพื้นที่แอดเดรสเสมือน
Yuval Itzchakov

ไม่แน่ใจเกี่ยวกับ 1 MB: บน Windows 8.1 ของฉันแอปพลิเคชันคอนโซล. NET Core 3.1 มี1572864ขนาดสแต็กเริ่มต้นเป็นไบต์ (ดึงข้อมูลโดยใช้ GetCurrentThreadStackLimits Win32 API) ฉันสามารถstackallocประมาณ1500000ไบต์โดยไม่ต้องใช้ StackOverflowException
George Chakhidze

คำตอบ:


210

ป้อนคำอธิบายภาพที่นี่

คุณกำลังมองหาผู้ชายที่เลือกนั้น David Cutler และทีมของเขาเลือกหนึ่งเมกะไบต์เป็นขนาดสแต็กเริ่มต้น ไม่มีส่วนเกี่ยวข้องกับ. NET หรือ C # แต่สิ่งนี้ถูกตอกลงเมื่อสร้าง Windows NT หนึ่งเมกะไบต์คือสิ่งที่เลือกเมื่อส่วนหัว EXE ของโปรแกรมหรือการเรียก winapi ของ CreateThread () ไม่ได้ระบุขนาดสแต็กอย่างชัดเจน ซึ่งเป็นวิธีปกติโปรแกรมเมอร์เกือบทุกคนทิ้งระบบปฏิบัติการเพื่อเลือกขนาด

ทางเลือกนั้นอาจจะมีการออกแบบ Windows NT ไว้ล่วงหน้าประวัติก็มืดมนเกินไปเกี่ยวกับเรื่องนี้ คงจะดีถ้ามีดจะเขียนหนังสือเกี่ยวกับเรื่องนี้ แต่เขาไม่เคยเป็นนักเขียน เขามีอิทธิพลอย่างมากต่อการทำงานของคอมพิวเตอร์ การออกแบบระบบปฏิบัติการครั้งแรกของเขาคือ RSX-11M ซึ่งเป็นระบบปฏิบัติการ 16 บิตสำหรับคอมพิวเตอร์ DEC (Digital Equipment Corporation) มันมีอิทธิพลอย่างมากต่อ CP / M ของ Gary Kildall ซึ่งเป็นระบบปฏิบัติการที่ดีตัวแรกสำหรับไมโครโปรเซสเซอร์ 8 บิต ซึ่งมีอิทธิพลอย่างมากต่อ MS-DOS

การออกแบบต่อไปของเขาคือ VMS ซึ่งเป็นระบบปฏิบัติการสำหรับโปรเซสเซอร์ 32 บิตที่รองรับหน่วยความจำเสมือน ประสบความสำเร็จมาก DEC คนต่อไปของเขาถูกยกเลิกโดย DEC ในช่วงที่ บริษัท เริ่มสลายตัวโดยไม่สามารถแข่งขันกับฮาร์ดแวร์พีซีราคาถูกได้ คิว Microsoft พวกเขายื่นข้อเสนอที่เขาไม่สามารถปฏิเสธได้ เพื่อนร่วมงานของเขาหลายคนเข้าร่วมด้วย พวกเขาทำงานบน VMS v2 หรือที่รู้จักกันดีในชื่อ Windows NT ธ . ค. ไม่พอใจเงินเปลี่ยนมือเพื่อชำระหนี้ ไม่ว่า VMS จะเลือกหนึ่งเมกะไบต์แล้วหรือยังฉันไม่รู้ แต่ฉันรู้จัก RSX-11 ดีพอ มันไม่น่าเป็นไปได้

พอมีประวัติ. หนึ่งเมกะไบต์เป็นจำนวนมากเธรดจริงแทบจะไม่กินเนื้อที่มากกว่าสองกำมือของกิโลไบต์ เมกะไบต์จึงค่อนข้างสิ้นเปลือง แต่มันเป็นชนิดของเสียที่คุณสามารถจ่ายได้บนระบบปฏิบัติการความต้องการเพจหน่วยความจำเสมือนเมกะไบต์ที่เป็นเพียงหน่วยความจำเสมือน เพียงแค่ตัวเลขไปยังโปรเซสเซอร์แต่ละตัวสำหรับทุกๆ 4096 ไบต์ คุณไม่เคยใช้หน่วยความจำจริงแรมในเครื่องจนกว่าคุณจะจัดการกับมันจริงๆ

โปรแกรม. NET มีมากเกินไปเนื่องจากเดิมมีการเลือกขนาดหนึ่งเมกะไบต์เพื่อรองรับโปรแกรมเนทีฟ ซึ่งมักจะสร้างสแต็กเฟรมขนาดใหญ่เก็บสตริงและบัฟเฟอร์ (อาร์เรย์) บนสแตกด้วย น่าอับอายในการเป็นเวกเตอร์การโจมตีของมัลแวร์บัฟเฟอร์ล้นสามารถจัดการโปรแกรมด้วยข้อมูล ไม่ใช่วิธีการทำงานของโปรแกรม. NET สตริงและอาร์เรย์จะถูกจัดสรรบนฮีป GC และการจัดทำดัชนีจะถูกตรวจสอบ วิธีเดียวที่จะจัดสรรพื้นที่ในกองด้วย C # นั้นอยู่กับคนที่ไม่ปลอดภัยstackallocคำหลัก

การใช้งานสแต็กใน. NET ที่ไม่สำคัญเพียงอย่างเดียวคือการกระวนกระวายใจ ใช้สแต็กของเธรดของคุณเพื่อคอมไพล์ MSIL เข้ากับรหัสเครื่อง ฉันไม่เคยเห็นหรือตรวจสอบว่าต้องใช้พื้นที่เท่าใด แต่ขึ้นอยู่กับลักษณะของโค้ดและเปิดใช้งานเครื่องมือเพิ่มประสิทธิภาพหรือไม่ แต่สองสามสิบกิโลไบต์เป็นการเดาคร่าวๆ ซึ่งไม่เช่นนั้นเว็บไซต์นี้มีชื่ออย่างไรสแตกล้นในโปรแกรม. NET นั้นค่อนข้างร้ายแรง มีพื้นที่ว่างไม่เพียงพอ (น้อยกว่า 3 กิโลไบต์) ที่จะยังคง JIT โค้ดที่พยายามจับข้อยกเว้นได้อย่างน่าเชื่อถือ Kaboom ไปยังเดสก์ท็อปเป็นตัวเลือกเดียว

สุดท้าย แต่ไม่ท้ายสุดโปรแกรม. NET ทำสิ่งที่ไม่เป็นประโยชน์กับสแต็ก CLR จะคอมมิตสแต็กของเธรด นั่นเป็นคำที่มีราคาแพงซึ่งหมายความว่าไม่เพียงแค่สงวนขนาดของสแต็กเท่านั้น แต่ยังตรวจสอบให้แน่ใจว่ามีการสงวนพื้นที่ไว้ในไฟล์เพจของระบบปฏิบัติการเพื่อให้สามารถสลับสแตกได้เมื่อจำเป็น การไม่ยอมรับถือเป็นข้อผิดพลาดร้ายแรงและยุติโปรแกรมโดยไม่มีเงื่อนไข สิ่งนี้เกิดขึ้นเฉพาะกับเครื่องที่มี RAM น้อยมากซึ่งรันกระบวนการมากเกินไปโดยสิ้นเชิงเครื่องดังกล่าวจะเปลี่ยนเป็นกากน้ำตาลก่อนที่โปรแกรมจะเริ่มตาย ปัญหาที่อาจเกิดขึ้นเมื่อ 15 ปีก่อนไม่ใช่วันนี้ โปรแกรมเมอร์ที่ปรับแต่งโปรแกรมให้ทำหน้าที่เหมือนรถแข่ง F1 ใช้<disableCommitThreadStack>องค์ประกอบในไฟล์. config

Fwiw, Cutler ไม่ได้หยุดออกแบบระบบปฏิบัติการ ภาพนั้นถูกสร้างขึ้นในขณะที่เขาทำงานกับ Azure


อัปเดตฉันสังเกตเห็นว่า. NET ไม่คอมมิตสแต็กอีกต่อไป ไม่แน่ใจว่าเกิดขึ้นเมื่อใดหรือเพราะเหตุใดฉันตรวจสอบนานเกินไปแล้ว ฉันเดาว่าการเปลี่ยนแปลงการออกแบบนี้เกิดขึ้นที่ไหนสักแห่งใน. NET 4.5 การเปลี่ยนแปลงที่สมเหตุสมผล


3
เขียนถึงความคิดเห็นของคุณThe only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.- ตัวแปรภายในเช่นถูกintประกาศไว้ในเมธอดที่ไม่ได้เก็บไว้ในสแต็กหรือไม่? ฉันคิดว่าพวกเขาเป็น
RBT

2
ตกลง. ตอนนี้ฉันเข้าใจแล้วว่าสแต็กเฟรมไม่ใช่ทางเลือกเดียวในการจัดเก็บสำหรับตัวแปรภายในของฟังก์ชัน มันสามารถเก็บไว้ในกรอบกอง แต่เป็นปัญหาหนึ่งในจุด bullet ของคุณ ฮันส์ที่ให้ความกระจ่างมาก ฉันไม่สามารถพูดขอบคุณมากพอสำหรับการเขียนโพสต์เชิงลึกเช่นนี้ จริงๆแล้วสแต็คเป็นนามธรรมที่ยิ่งใหญ่สำหรับการเขียนโปรแกรมโดยทั่วไปเพียงเพื่อหลีกเลี่ยงความซับซ้อนที่ไม่จำเป็น
RBT

บรรยายละเอียดมาก @Hans. ฉันแค่สงสัยว่าค่าต่ำสุดที่เป็นไปได้สำหรับmaxStackSizeเธรดคืออะไร? ฉันไม่พบข้อมูลนี้ใน [MSDN] ( msdn.microsoft.com/en-us/library/5cykbwz4(v=vs.110).aspx ) จากความคิดเห็นของคุณดูเหมือนว่าการใช้งานสแต็กจะต่ำสุดแน่นอนและฉันสามารถใช้ค่าที่น้อยที่สุดเพื่อรองรับเธรดสูงสุดที่เป็นไปได้ ขอบคุณ.
MKR

1
@KFL: คุณสามารถตอบคำถามของคุณได้อย่างง่ายดายโดยการลอง!
Eric Lippert

1
หากพฤติกรรมเริ่มต้นคือไม่ยอมรับสแต็กอีกต่อไปไฟล์ markdown นี้จะต้องได้รับการแก้ไขgithub.com/dotnet/docs/blob/master/docs/framework/…
John Stewien

5

ขนาดสแต็กที่สงวนไว้เริ่มต้นถูกระบุโดยตัวเชื่อมโยงและนักพัฒนาสามารถแทนที่ด้วยการเปลี่ยนค่า PE ในเวลาลิงก์หรือสำหรับแต่ละเธรดโดยระบุdwStackSizeพารามิเตอร์สำหรับCreateThreadฟังก์ชัน WinAPI

หากคุณสร้างเธรดที่มีขนาดสแต็กเริ่มต้นใหญ่กว่าหรือเท่ากับขนาดสแต็กเริ่มต้นมันจะปัดเศษขึ้นเป็นจำนวนทวีคูณที่ใกล้ที่สุด 1 MB

เหตุใดค่าจึงเท่ากับ 1 MB สำหรับกระบวนการ 32 บิตและ 4 MB สำหรับ 64 บิต ฉันคิดว่าคุณควรถามนักพัฒนาผู้ออกแบบ Windows หรือรอจนกว่าจะมีคนตอบคำถามของคุณ

มาร์ครัสซิโนวิชอาจรู้เรื่องนี้และคุณสามารถติดต่อเขาได้ บางทีคุณอาจจะสามารถหาข้อมูลนี้ในหนังสือของ Windows Internals ของเขาก่อนหน้านี้กว่ารุ่นที่หกซึ่งอธิบายข้อมูลน้อยเกี่ยวกับสแต็คของเขามากกว่าบทความ หรือบางที Raymond Chen ก็รู้เหตุผลเพราะเขาเขียนสิ่งที่น่าสนใจเกี่ยวกับ Windows internalals และประวัติของมัน เขาสามารถตอบคำถามของคุณมากเกินไป แต่คุณควรโพสต์ข้อเสนอแนะไปยังห้องนั่งเล่น

แต่ในเวลานี้ฉันจะพยายามอธิบายเหตุผลที่เป็นไปได้ว่าทำไม Microsoft จึงเลือกค่าเหล่านี้โดยใช้บล็อกของ MSDN, Mark และ Raymond

ค่าดีฟอลต์มีค่าเหล่านี้อาจเป็นเพราะในยุคแรกพีซีทำงานช้าและการจัดสรรหน่วยความจำบนสแตกนั้นเร็วกว่าการจัดสรรหน่วยความจำในฮีปมาก และเนื่องจากการจัดสรรสแต็กมีราคาถูกกว่ามากจึงใช้ แต่ต้องมีขนาดสแต็กที่ใหญ่กว่า

ดังนั้นค่าจึงเป็นขนาดสแต็กที่สงวนไว้ที่เหมาะสมที่สุดสำหรับแอปพลิเคชันส่วนใหญ่ เป็นวิธีที่ดีที่สุดเนื่องจากอนุญาตให้ทำการโทรแบบซ้อนกันจำนวนมากและจัดสรรหน่วยความจำบนสแตกเพื่อส่งผ่านโครงสร้างไปยังฟังก์ชันการโทร ในขณะเดียวกันก็อนุญาตให้สร้างเธรดจำนวนมาก

ปัจจุบันค่าเหล่านี้ส่วนใหญ่ใช้สำหรับความเข้ากันได้แบบย้อนหลังเนื่องจากโครงสร้างที่ส่งผ่านเป็นพารามิเตอร์ไปยังฟังก์ชัน WinAPI ยังคงถูกจัดสรรบนสแต็ก แต่ถ้าคุณไม่ได้ใช้การจัดสรรสแต็กการใช้งานสแต็กของเธรดจะน้อยกว่าค่าเริ่มต้น 1 MB อย่างมากและเป็นการสิ้นเปลืองตามที่ Hans Passant กล่าวไว้ และเพื่อป้องกันไม่ให้ระบบปฏิบัติการนี้กำหนดเฉพาะหน้าแรกของสแต็ก (4 KB) หากไม่ได้ระบุไว้ในส่วนหัว PE ของแอปพลิเคชัน หน้าอื่น ๆ จะถูกจัดสรรตามความต้องการ

แอปพลิเคชั่นบางตัวจะแทนที่พื้นที่ที่อยู่ที่สงวนไว้และเริ่มต้นที่จะเพิ่มประสิทธิภาพการใช้งานหน่วยความจำ ตัวอย่างเช่นขนาดสแต็กสูงสุดของเธรดของกระบวนการเนทีฟ IIS คือ 256 KB ( KB932909 ) และการลดลงของค่าเริ่มต้นนี้แนะนำโดย Microsoft:

ที่ดีที่สุดคือเลือกขนาดสแต็กที่เล็กที่สุดและยอมรับสแต็กที่จำเป็นเพื่อให้เธรดหรือไฟเบอร์ทำงานได้อย่างน่าเชื่อถือ ทุกเพจที่สงวนไว้สำหรับสแต็กไม่สามารถใช้เพื่อวัตถุประสงค์อื่นใด

แหล่งที่มา:

  1. ขนาดชุดเธรด (Microsoft Docs)
  2. การผลักดันขีด จำกัด ของ Windows: กระบวนการและเธรด (Mark Russinovich)
  3. ตามค่าเริ่มต้นขนาดสแต็กสูงสุดของเธรดที่สร้างขึ้นในกระบวนการ IIS ดั้งเดิมคือ 256 KB (KB932909)

ถ้าฉันต้องการขนาดสแต็กที่ใหญ่ขึ้นฉันสามารถตั้งค่าได้ ( atalasoft.com/cs/blogs/rickm/archive/2008/04/22/… ) ฉันต้องการทราบข้อพิจารณาและเหตุผลที่อยู่เบื้องหลังการตัดสินใจนั้น
Nikolay Kostov

2
ตกลง. ตอนนี้ฉันเข้าใจคุณแล้ว :) ขนาดสแต็กเริ่มต้นควรเหมาะสมที่สุด (ดูความคิดเห็นของ @Lucas Trzesniewski) และควรปัดเศษให้เป็นผลคูณที่ใกล้เคียงที่สุดของรายละเอียดการจัดสรร หากขนาดสแต็กที่ระบุมีขนาดใหญ่กว่าขนาดสแต็กเริ่มต้นจะปัดเศษขึ้นเป็นจำนวน 1MB ที่ใกล้ที่สุด ดังนั้น Microsoft จึงเลือกขนาดนี้เป็นขนาดสแต็กเริ่มต้นสำหรับแอปพลิเคชันโหมดผู้ใช้ทั้งหมด และไม่มีเหตุผลอื่นใด
Yoh Deadfall

แหล่งที่มา? เอกสารใด ๆ :)
Nikolay Kostov

@ ลิงค์ที่น่าสนใจ คุณควรสรุปเป็นคำตอบของคุณ
Lucas Trzesniewski
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.