เกม C ++ จัดการกับการจัดสรรหน่วยความจำล้มเหลวได้อย่างไร


23

ฉันตระหนักถึงหลายเกมที่เขียนด้วย C ++ แต่ไม่ได้ใช้ข้อยกเว้น เนื่องจากการจัดการกับความล้มเหลวในการจัดสรรหน่วยความจำใน C ++ นั้นโดยทั่วไปถูกสร้างขึ้นโดยมีstd::bad_allocข้อยกเว้นเกมเหล่านี้จะจัดการกับความล้มเหลวได้อย่างไร?

พวกเขาเพียงแค่ผิดพลาดหรือมีวิธีการจัดการและกู้คืนจากข้อผิดพลาดออกจำ?


1
โปรดทราบว่ามีบางสภาพแวดล้อม / ระบบปฏิบัติการที่การจัดสรรอ้างว่าประสบความสำเร็จ แต่พยายามใช้หน่วยความจำขัดข้องโปรแกรม (หรืออื่น ๆ ) ของคุณ
PlasmaHH

6
หากการจัดสรรล้มเหลวระหว่างเกม Quake 3 จะนำคุณกลับไปที่เมนูพร้อมข้อความแสดงข้อผิดพลาด ฉันเชื่อว่าสิ่งนี้เกิดขึ้นได้โดย Quake 3 ของตัวจัดสรรพูลซึ่งสามารถปล่อยพูลทั้งหมดและกลับสู่เมนูได้อย่างปลอดภัยหากพูลหมด
Dietrich Epp

16
โดยปกติแล้วการกระแทก
user253751

1
หากมีข้อยกเว้นถูกปิดใช้งานอย่างแท้จริงโปรแกรมจะต้องโทรแทนstd::terminateและนั่นคือมัน แต่ภายใต้ข้อ จำกัด เดียวกันการจัดสรรสามารถส่งคืนพอยน์เตอร์พอยน์เตอร์แทนการโยนข้อยกเว้นและผลลัพธ์นั้นสามารถตรวจสอบและจัดการแยกกันได้
underscore_d

2
ใน C ++ คุณสามารถพูดได้ClassName variableName = new(nothrow) ClassName();( แทนที่ชื่อคลาสชื่อตัวแปร ฯลฯ ) จากนั้นหากการจัดสรรล้มเหลวคุณสามารถตรวจสอบได้โดยบอกว่าif(!variableName)อนุญาตให้จัดการข้อผิดพลาดได้โดยไม่ต้องใช้บล็อกข้อยกเว้น try-catch ในทำนองเดียวกันหากหน่วยความจำถูกจัดสรรโดยใช้ฟังก์ชั่นเช่นmalloc(), calloc()ฯลฯ จะสามารถตรวจพบความล้มเหลวในการจัดสรรโดยใช้if(!variableName)วิธีการเดียวกันโดยไม่ต้องลองจับ สำหรับวิธีที่เกมจัดการกับข้อผิดพลาดเหล่านี้ในจุดนั้นมันขึ้นอยู่กับเกมที่จะตัดสินใจว่ามันจะล้มเหลวหรือไม่
Spencer D

คำตอบ:


63

เช่นเดียวกับโปรแกรมเฉลี่ยทั้งหมด: พวกเขาไม่ได้ *

สำหรับแอปพลิเคชั่นส่วนใหญ่ไม่มีลูกค้าคาดหวังว่าพวกเขาจะทำงานต่อไปเมื่อหน่วยความจำหมด เกมทั้งหมดอยู่ภายใต้ "แอปพลิเคชันส่วนใหญ่" เหล่านี้ การใช้เวลาและเงินเพื่อทำงานในกรณีที่ลูกค้าไม่คาดหวังในการทำงานจะไม่สมเหตุสมผล

คำถามคล้ายกับต่อไปนี้:

  • คุณจะติดตั้งเกมได้อย่างไรหากฮาร์ดดิสก์เต็ม?
  • คุณยังคงรันเกมด้วยอัตราเฟรมต่อวินาทีสูงบนพีซีต่ำกว่าข้อกำหนดขั้นต่ำได้อย่างไร

คำตอบเหมือนกัน: นี่คือปัญหาของผู้ใช้ เกมไม่สนใจ


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


2
ในการจัดสรรหน่วยความจำล่วงหน้าสำหรับการบันทึก ฯลฯ ในกรณีที่มีการจัดสรรหน่วยความจำล้มเหลว: stackoverflow.com/questions/7749066/…
bwDraco

23

โดยทั่วไปแล้วสถานการณ์ประเภทนี้จะไม่เกิดขึ้น

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

ทั้งหมดนี้เป็นเรื่องดีและดี แต่มันใช้ไม่ได้กับคอนโซลหรือระบบปฏิบัติการรุ่นเก่าดังนั้นอย่างที่สองเกมไม่ได้ทำการจัดสรรแบบไดนามิกขนาดเล็กจำนวนมากโดยใช้ตัวจัดสรรไลบรารีมาตรฐาน แต่สิ่งที่พวกเขาจะทำคือจัดสรรพูลหน่วยความจำขนาดใหญ่เพียงครั้งเดียวเท่านั้นเมื่อเริ่มต้นและดึงลงมาจากกลุ่มนั้นสำหรับการจัดสรรรันไทม์ใด ๆ ที่จำเป็น (เช่นโดยใช้ตำแหน่ง C ++ ใหม่หรือเขียนระบบการจัดการหน่วยความจำของตนเอง ด้านบนของมัน)

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

และประการที่สามเกมจะกำหนดสเปคขั้นต่ำและกำหนดงบประมาณการใช้หน่วยความจำภายใน ดังนั้นหากมีการระบุเกมที่ต้องการ RAM ขนาด 1GB นั่นหมายความว่าตราบใดที่การใช้หน่วยความจำของมันไม่เกิน 1GB ก็ไม่ต้องกังวลว่า RAM จะหมด


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

@immibis - โปรดดูจุดที่สามของฉัน
Maximus Minimus

2
@immibis คุณหมายถึง ... พวกเขาจะทำอย่างไรถ้าสระว่ายน้ำว่างเปล่า? ซึ่งแตกต่างจากหน่วยความจำของระบบขนาดและการใช้งานของพูลนั้นอยู่ภายใต้การควบคุมของ ดังนั้นกลุ่มจะไม่สามารถว่างเปล่าได้หากแอปพลิเคชันมีข้อบกพร่อง
David Schwartz

@DavidSchwartz หรือเว้นเสีย แต่เคอร์เนลกำลังใช้หน่วยความจำมากเกินไป Linux ทำสิ่งนี้ แม้การ zeroing out pool ของคุณจะไม่ช่วยถ้าเปิดใช้งานการบีบอัด pagefile ผ่าน zswap หรือ zram
Damian Yerrick

1
สิ่งนี้ดูเหมือนจะไม่ตอบคำถามจริง แต่แทนที่จะบอกบางสิ่งที่คล้ายกับ "เรือลำนี้ไม่สามารถใช้ได้ดังนั้นเราจะต้องมีกระบวนการฉุกเฉินสำหรับอะไร"
Lilienthal

2

ส่วนใหญ่เป็นวิธีเดียวกับที่เราทำก่อนที่จะมีข้อยกเว้น - "ตรวจสอบวิธีคืนค่า" แบบเก่า หากตัวจัดสรรไม่ได้ใช้ข้อยกเว้นปกติจะส่งคืนnullเมื่อการจัดสรรล้มเหลว เกมที่เขียนอย่างดีจะตรวจสอบเรื่องนั้นและจัดการกับสถานการณ์ด้วยวิธีที่ปลอดภัย บางครั้งนี่หมายถึงการยกเลิกเกม (เช่นstd::terminate) หรืออย่างน้อยการย้อนกลับสู่สถานะความปลอดภัยล่าสุดที่เป็นที่รู้จัก (เช่นเมื่อคุณจัดสรรจากพูลคุณสามารถกำจัดพูลทั้งหมดได้อย่างปลอดภัยแม้ว่าจะอยู่ในสถานะที่ไม่ปลอดภัย)

เกมหลายเกมใช้ข้อยกเว้นสำหรับกรณีเช่นนี้แม้กระทั่ง เมื่อมีคนพูดว่า "หลีกเลี่ยงการใช้ข้อยกเว้นในรหัสเกม" สิ่งที่พวกเขามักจะหมายถึงคือ "ใช้ข้อยกเว้นสำหรับกรณีพิเศษอย่างแท้จริงไม่ใช่เพื่อการควบคุมการไหลของโลกีย์" การตั้งค่าสำหรับการยกเว้นหน่วยความจำไม่เพียงพอนั้นมีราคาถูก - ค่าใช้จ่ายส่วนใหญ่อยู่ในระยะโยนและภาวะแทรกซ้อนที่คุณได้รับจากการจัดการข้อยกเว้น ทั้งสองอย่างนั้นมีความสำคัญมากในกรณีที่หน่วยความจำไม่พอคุณมักจะต้องการที่จะยกเลิก

ใจคุณเกมส่วนใหญ่ก็จะพัง สิ่งนี้ไม่ได้บ้าอย่างที่คิด - คุณมักจะมีการใช้หน่วยความจำเป็นเป้าหมายและตรวจสอบให้แน่ใจว่าเกมของคุณไม่ถึงขีด จำกัด นั้น (ตัวอย่างเช่นโดยใช้การ จำกัด จำนวนหน่วยในเกม RTS หรือ จำกัด จำนวนศัตรูในส่วนของ FPS) แม้ว่าคุณจะไม่ทำเช่นนั้น แต่โดยปกติคุณจะไม่ได้ใช้หน่วยความจำไม่เพียงพอในระบบที่ทันสมัยพอสมควรเช่นพื้นที่ที่อยู่เสมือนของคุณ (ในแอพพลิเคชั่น 32 บิต) หรือสแต็ก ระบบปฏิบัติการหลอกว่าคุณมีหน่วยความจำมากเท่าที่คุณต้องการ - นั่นเป็นหนึ่งในหน่วยความจำเสมือนที่เป็นนามธรรม ประเด็นก็คือเพราะคุณมี RAM จริงอยู่ที่ 256 MiB ไม่ได้หมายความว่าแอปพลิเคชันของคุณไม่สามารถใช้หน่วยความจำ 4 GiB ได้ เกมบางเกมอาจไม่ได้คิดมากเกินไปว่าข้อมูลทั้งหมดของพวกเขาจะไม่ '


1

การจับข้อยกเว้นและการตัดสินใจว่าจะทำอะไรเมื่อใดและยกข้อยกเว้นเป็นสองสิ่งที่แตกต่างกัน

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

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


หากเกมที่สงสัยว่า "ไม่ใช้ข้อยกเว้น" ตามที่ OP กล่าวไว้แสดงว่าพวกเขาไม่สามารถจับยกหรือประมวลผลเกมหนึ่งได้ หากมีข้อยกเว้นถูกปิดใช้งานและมีคนโยนหนึ่งโปรแกรมจะต้องโทรหาstd::terminateและนั่นคือมัน แต่ภายใต้เงื่อนไขดังกล่าวการจัดสรรสามารถคืนค่าตัวชี้โมฆะแทนการโยนข้อยกเว้นและสามารถจัดการแยกต่างหาก
underscore_d
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.