การใช้สแต็คมีมากเกินไปมากเกินไป?


22

เมื่อเร็ว ๆ นี้เมื่อฉันเขียน C หรือ C ++ ฉันจะประกาศตัวแปรทั้งหมดของฉันในสแต็คเพียงเพราะเป็นตัวเลือกซึ่งแตกต่างจาก Java

อย่างไรก็ตามฉันได้ยินมาว่าเป็นความคิดที่ดีที่จะประกาศเรื่องใหญ่ ๆ ในกองซ้อน

  1. ทำไมเป็นกรณีนี้ ฉันคิดว่าสแต็กล้นมีส่วนเกี่ยวข้อง แต่ฉันไม่ชัดเจนว่าทำไมจึงเกิดขึ้น
  2. สแต็คมีมากน้อยเพียงใด?

ฉันไม่ได้พยายามที่จะวางไฟล์ 100MB ในสแต็กเพียงหนึ่งโหลกิโลไบต์อาร์เรย์เพื่อใช้เป็นบัฟเฟอร์สตริงหรืออะไรก็ตาม มีการใช้งานสแต็กมากเกินไปหรือไม่

(ขออภัยหากซ้ำกันการค้นหาสแต็กยังคงให้การอ้างอิงถึงสแต็คโอเวอร์โฟลไม่มีแม้แต่สแต็กแท็กการโทรฉันเพิ่งใช้นามธรรมหนึ่ง)


1
คุณจะ "วางไฟล์ 100MB ในสแต็ค" ได้อย่างไร การใช้งานบัฟเฟอร์และคอนเทนเนอร์ (และคล้ายกันเช่น std :: string) มักจะใช้ heap เพื่อจัดเก็บข้อมูลของพวกเขา
เมอร์ฟี

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

3
ขอให้สังเกตว่า C & C ++ แตกต่างกัน std::vector<int>ตัวแปรในตัวเครื่องจะไม่กินพื้นที่สแต็กจำนวนมากข้อมูลส่วนใหญ่อยู่ในกอง
Basile Starynkevitch เมื่อ

คำตอบ:


18

ขึ้นอยู่กับระบบปฏิบัติการของคุณ บน Windows ขนาดสูงสุดโดยทั่วไปของสแต็กคือ 1MB ในขณะที่มันคือ 8MB บน Linux ทั่วไปที่ทันสมัยแม้ว่าค่าเหล่านั้นสามารถปรับได้หลายวิธี หากผลรวมของตัวแปรสแต็กของคุณ (รวมถึงค่าใช้จ่ายในระดับต่ำเช่นที่อยู่ผู้ส่งคืนอาร์กิวเมนต์ตามสแต็กตัวยึดค่าที่ส่งคืนและการจัดตำแหน่งไบต์) ในการโทรสแต็กทั้งหมดเกินกว่าขีด จำกัด นั้น โปรแกรมโดยไม่มีโอกาสกู้คืน

โดยปกติแล้วจะมีกี่กิโลไบต์ หลายสิบกิโลไบต์มีอันตรายเพราะเริ่มสรุป หลายร้อยกิโลไบต์เป็นความคิดที่แย่มาก


1
สแต็กทั่วไปไม่ จำกัดหลายเมกะไบต์ (เช่นมักจะมากกว่าหนึ่ง แต่อาจน้อยกว่าหนึ่งโหล) ในปี 2016 ในวันนี้ บนเดสก์ท็อป Linux ของฉันมันคือ 8Mbytes โดยค่าเริ่มต้น ...
Basile Starynkevitch

"ใน [ ... ] ลินุกซ์ขนาดสูงสุดทั่วไปสำหรับสแต็คเป็น 1MB" ผลตอบแทนของระบบอื่น ๆ$ ulimit -a ในกลุ่ม stack size (kbytes, -s) 8192
เมอร์ฟี

9

คำตอบที่ถูกต้องเพียงคำเดียวที่คลุมเครือ: "มากเกินไปคือเมื่อสแต็คล้น"

หากคุณไม่สามารถควบคุมการใช้งานโค้ดทุกบรรทัดระหว่างจุดเริ่มต้นของโปรแกรมและฟังก์ชั่นที่เป็นปัญหาได้อย่างสมบูรณ์คุณจะไม่สามารถตั้งสมมติฐานเกี่ยวกับจำนวนสแต็กที่มีอยู่ได้ ตัวอย่างเช่นคุณไม่สามารถรับประกันได้ว่าการเรียกใช้ฟังก์ชันนี้จะไม่ทำให้เกิดการล้นสแต็ก:

void break_the_camels_back()
{
    int straw;
    ...
}

ค่าเริ่มต้นของสแต็ค 8 MiB สำหรับ Unixes ที่ทันสมัยนั้นค่อนข้างมีพื้นที่เหลือเฟือโดยเฉพาะกับคนอย่างฉันที่มีคนแก่พอที่จะจำซีพียูที่มีพอยน์เตอร์สแต็ก 8 บิต ความเป็นจริงในทางปฏิบัติคือคุณไม่น่าจะผ่านพ้นมันไปได้โดยไม่ต้องลอง ถ้าคุณทำเกินขีด จำกัด สแต็กมักจะถือว่าเป็นการละเมิดการแบ่งส่วนและระบบที่มีการจัดการหน่วยความจำเพียงพอที่จะตรวจพบมันจะส่งSIGSEGVเมื่อมันเกิดขึ้น

คุณมีทางเลือกสองทาง อย่างแรกคืออย่าเดาว่าสแต็คมีจำนวนเท่าใดและถามระบบ สิ่งใดที่สอดคล้องกับ POSIX จะมีgetrlimit(2)ฟังก์ชั่นที่จะบอกคุณถึงขีด จำกัด สูงสุด RLIMIT_STACKเป็นขีด จำกัด เฉพาะที่คุณต้องการ ประการที่สองคือการตรวจสอบจำนวนโปรแกรมที่คุณใช้และทำการตัดสินใจเกี่ยวกับตัวแปรอัตโนมัติกับการจัดสรรหน่วยความจำแบบไดนามิกตามนั้น มีเท่าที่ฉันรู้ไม่มีฟังก์ชั่นมาตรฐานเพื่อกำหนดจำนวนกองที่ใช้ แต่โปรแกรมเช่นvalgrindสามารถวิเคราะห์ให้คุณ


4

หากคุณจัดสรรอาร์เรย์จำนวน 10,000 ไบต์บนสแต็กอาร์เรย์นั้นจะมีขนาด จำกัด 10,000 อาจมาก แต่ถ้าคุณต้องการ 10,001 ไบต์โปรแกรมของคุณอาจพังหรือแย่ลง ดังนั้นในสถานการณ์เช่นนี้คุณต้องการบางสิ่งบางอย่างที่ปรับให้เหมาะกับขนาดที่คุณต้องการและสิ่งที่จะไม่อยู่ในกองซ้อน

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

แต่ถ้าคุณใช้ C ++ และประกาศเช่น std :: string หรือ std :: vec บนสแต็กสิ่งที่อยู่บนสแต็กจะเป็นขนาดที่แน่นอนและเล็ก ข้อมูลจริงจะถูกเก็บไว้ในกอง คุณสามารถจัดเก็บหนึ่งล้านตัวอักษรใน std :: string instance และจะใช้ข้อมูลจำนวนน้อยมาก (โดยทั่วไปคือ 8 ถึง 24 ไบต์ขึ้นอยู่กับการนำไปใช้) บนสแต็กและหนึ่งล้านไบต์บนฮีป


2

1 MB เป็นค่าประมาณที่ดีสำหรับ * ระวัง การเรียกซ้ำอาจเป็นสาเหตุหลักของการล้นสแต็กร่วมกับการจัดสรรสแต็ก อย่างไรก็ตามในกรณีส่วนใหญ่วัตถุเทพเจ้าที่ดูเหมือนเผินๆเกินกว่าที่จะวางบนสแต็กได้รับการออกแบบมาอย่างดีเพื่อจัดการหน่วยความจำภายในของพวกเขาบนฮีปและใช้สแต็คเป็นวิธีในการทำลายโดยอัตโนมัติเมื่อสแตก destructor จะเพิ่มหน่วยความจำขนาดใหญ่ที่ถูกจัดการภายใน คอนเทนเนอร์ std ได้รับการออกแบบในลักษณะนั้นและพอยน์เตอร์ที่ใช้ร่วมกัน / ไม่ซ้ำกันได้รับการออกแบบในลักษณะนั้นเช่นกัน

สิ่งที่สำคัญคือไม่ต้องจัดสรร raw mem จำนวนมากบน stack เช่น char [1024 * 1024] และออกแบบคลาสเพื่อตัดการจัดสรร heap และใช้ stack เท่านั้นเพื่อความสะดวกในการเรียก destructor โดยอัตโนมัติ

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