C ++ गेम मेमोरी आवंटन विफलता को कैसे संभालते हैं?


23

मैं कई खेलों से अवगत हूं जो C ++ में लिखे गए हैं लेकिन अपवादों का उपयोग नहीं करते हैं। चूंकि C ++ में मेमोरी एलोकेशन फेलियर को आमतौर पर std::bad_allocअपवाद के आसपास बनाया गया है, ऐसे गेम इस तरह की विफलता को कैसे संभालते हैं?

क्या वे बस दुर्घटना करते हैं, या एक आउट-ऑफ-मेमोरी त्रुटि से निपटने और पुनर्प्राप्त करने का एक और तरीका है?


1
ध्यान दें कि कुछ निश्चित वातावरण / ओएस हैं जहां आवंटन सफल होने का दावा करता है, लेकिन मेमोरी का उपयोग करने की कोशिश आपके (या अन्य) प्रोग्राम को क्रैश कर देती है
प्लाज्माएचएच

6
यदि खेल के दौरान आवंटन विफल रहता है, तो Quake 3 आपको एक त्रुटि संदेश के साथ मेनू पर वापस ले जाएगा। मेरा मानना ​​है कि यह क्वेक 3 के पूल एलोकेटर द्वारा संभव बनाया गया है, जो कि पूरे पूल को गिरा सकता है और यदि पूल से बाहर निकलता है तो सुरक्षित रूप से मेनू पर वापस आ सकता है।
डेट्रिच एप्प

16
आमतौर पर, दुर्घटनाग्रस्त होकर।
user253751

1
यदि अपवाद वास्तव में अक्षम हैं, तो प्रोग्राम को कॉल करने के बजाय std::terminate, और यह होना चाहिए । लेकिन फिर एक ही प्रतिबंध के तहत, आवंटन अपवाद को फेंकने के बजाय एक अशक्त सूचक लौटा सकता है, और उस परिणाम को अलग से जांचा और नियंत्रित किया जा सकता है।
अंडरस्कोर_ड

2
C ++ में, आप कह सकते हैं ClassName variableName = new(nothrow) ClassName();( स्पष्ट रूप से वर्ग का नाम, चर नाम, आदि की जगह ) फिर, यदि आवंटन विफल रहता है, तो आप यह कहकर इसका पता लगा सकते हैं if(!variableName)कि त्रुटि को बिना किसी प्रयास के अपवाद ब्लॉक को संभालने की अनुमति देता है। इसी तरह, यदि मेमोरी को किसी फंक्शन जैसे malloc(), calloc()इत्यादि का उपयोग करके आवंटित किया जाता है , तो if(!variableName)कोशिश करने के लिए एक ही विधि का उपयोग किए बिना एक ही विधि का उपयोग करके आवंटित करने में विफलताओं का पता लगाया जा सकता है । जैसे ही गेम इन त्रुटियों को कैसे संभालता है, ठीक है, उस बिंदु पर, यह गेम devs पर निर्भर करता है कि वह क्रैश करता है या नहीं।
स्पेंसर डी

जवाबों:


63

सभी औसत कार्यक्रमों के समान: वे नहीं। *

अधिकांश अनुप्रयोगों के लिए ग्राहक की कोई अपेक्षा नहीं है कि वे एक बार मेमोरी चलाने के बाद भी काम करना जारी रखें। सभी खेल इन "अधिकांश अनुप्रयोगों" के अंतर्गत आते हैं। एक किनारे के मामले पर काम करने के लिए समय और पैसा खर्च करना जो ग्राहक को काम करने की उम्मीद नहीं करता है, इससे कोई मतलब नहीं है।

प्रश्न निम्नलिखित के समान है:

  • यदि हार्डडिस्क भरा हुआ है तो आप गेम कैसे इंस्टॉल करते हैं?
  • आप अभी भी गेम को उच्च एफपीएस पर न्यूनतम ऐनक से नीचे पीसी पर कैसे चलाते हैं?

इसका उत्तर समान है: ये उपयोगकर्ताओं की समस्याएं हैं। खेल परवाह नहीं है।


* वास्तव में, वे आमतौर पर अपवाद का एक उच्च स्तर पकड़ लेते हैं, और स्मृति का उपयोग करते हैं जो खेल के शुरू में पूर्व-आवंटित किया गया था ताकि दुर्घटना / समाप्ति से पहले घटना को लॉग इन करने का प्रयास किया जा सके। लॉग तब ग्राहक सहायता को समस्या पर कम समय बर्बाद करने की अनुमति देता है।


2
स्मृति आबंटन विफलता की स्थिति में लॉगिंग इत्यादि के लिए
प्रचार करने पर

23

आमतौर पर इस तरह का परिदृश्य कभी नहीं होता है।

सबसे पहले, आधुनिक ऑपरेटिंग सिस्टम पर वर्चुअल मेमोरी का मतलब है कि यह सामान्य ऑपरेशन में वैसे भी होने की अत्यधिक संभावना नहीं है; जब तक आपके पास भगोड़ा आवंटन बग नहीं होता है, खेल OSs वर्चुअल एड्रेस स्पेस से मेमोरी आवंटित करेगा और OS अंदर और बाहर पेजिंग के बाद देखेगा।

यह सब अच्छी तरह से और अच्छा है, लेकिन यह वास्तव में कंसोल या पुराने ओएस पर लागू नहीं होता है, इसलिए दूसरी बात यह है कि गेम वैसे भी मानक पुस्तकालय आवंटन का उपयोग करके बहुत सारे छोटे गतिशील आवंटन नहीं करते हैं। इसके बजाय वे क्या करेंगे स्मृति के एक बड़े पूल को केवल एक बार स्टार्टअप पर आवंटित करें, और किसी भी रनटाइम आवंटन के लिए उस पूल से नीचे आकर्षित करें जो आवश्यक हैं (उदाहरण के लिए C ++ प्लेसमेंट नए का उपयोग करके या अपनी स्मृति प्रबंधन प्रणाली लिखकर इसके ऊपर)।

यह भी मेमोरी लीक से बचाता है क्योंकि प्रत्येक छोटे से व्यक्तिगत आवंटन के लिए ट्रैक करने और खाते के बजाय, एक गेम मेमोरी को पुनः प्राप्त करने के लिए पूरे पूल को फेंक सकता है।

और तीसरा, खेल हमेशा एक न्यूनतम विनिर्देश निर्धारित करेंगे और उसके भीतर उनके मेमोरी उपयोग को बजट देंगे। इसलिए यदि किसी गेम को 1GB RAM की आवश्यकता के लिए निर्दिष्ट किया जाता है, तो इसका मतलब है कि जब तक इसकी मेमोरी का उपयोग 1gb से अधिक नहीं हो जाता है, तब तक इसे रैम से बाहर चलने के बारे में चिंता करने की आवश्यकता नहीं है।


3
"इसके बजाय वे क्या करेंगे स्मृति के एक बड़े पूल को केवल एक बार स्टार्टअप पर आवंटित करें, और किसी भी रनटाइम आवंटन के लिए उस पूल से नीचे खींचें जो आवश्यक हैं" - और क्या आपको लगता है कि पूल भरा हुआ है?
user253751

@immibis - कृपया मेरी तीसरी बात देखें।
मैक्सिमस मिनिमस

2
@immibis आपका मतलब है ... यदि पूल खाली है तो वे क्या करते हैं? सिस्टम मेमोरी के विपरीत, पूल का आकार और उपयोग पूरी तरह से एप्लिकेशन के नियंत्रण में है। जब तक कि एप्लिकेशन में बग हो , पूल खाली नहीं हो सकता
डेविड श्वार्ट्ज

@DavidSchwartz या जब तक कर्नेल मेमोरी ओवरकॉमिट का उपयोग नहीं कर रहा है। लिनक्स ऐसा करता है। यहां तक ​​कि अपने पूल को शून्य करने से भी मदद नहीं मिलती है अगर पेजफाइल संपीड़न को zswap या zram के माध्यम से सक्षम किया गया है।
डेमियन येरिक

1
यह वास्तविक प्रश्न का उत्तर देने के लिए प्रतीत नहीं होता है, बल्कि इसके समान कुछ कहता है "यह जहाज अकल्पनीय है इसलिए हमें इसके लिए एक आपातकालीन प्रक्रिया की क्या आवश्यकता होगी?"
लिलिएनथल

2

खैर, मुख्य रूप से, उसी तरह जो हमने अपवादों से पहले किया था - पुराना "रिटर्न वैल्यू दृष्टिकोण की जांच करें"। यदि कोई आवंटनकर्ता अपवादों का उपयोग नहीं करता है, तो यह आम तौर पर nullतब होगा जब आवंटन विफल हो जाएगा। एक अच्छी तरह से लिखा गया खेल उसके लिए जाँच करेगा और जो भी सुरक्षित है उस स्थिति को संभाल लेगा। कभी-कभी, इसका मतलब है कि खेल को समाप्त करना (उदाहरण के लिए std::terminate) या कम से कम अंतिम ज्ञात सुरक्षित स्थिति में उदासीन होना (जैसे जब आप एक पूल से आवंटित करते हैं, तो आप सुरक्षित रूप से पूरे पूल का निपटान तब भी कर सकते हैं, जब वह असुरक्षित स्थिति में हो)।

कई खेल इस तरह के मामलों के लिए अपवादों का उपयोग करते हैं तब भी। जब कोई कहता है कि "गेम कोड में अपवादों का उपयोग करने से बचें", तो उनका आमतौर पर मतलब यह है कि "केवल असाधारण मामलों के लिए अपवादों का उपयोग करें, सांसारिक प्रवाह नियंत्रण के लिए नहीं"। मेमोरी अपवाद से बाहर पकड़ने के लिए सेटअप सस्ता है - लागत ज्यादातर फेंक में है, और अपवाद को संभालने के साथ आपको मिलने वाली जटिलताओं। याददाश्त के मामले में दोनों में से कोई भी बहुत महत्वपूर्ण नहीं है - आप लगभग हमेशा वैसे भी समाप्त करना चाहते हैं।

आप का ध्यान रखें, अधिकांश खेल बस दुर्घटनाग्रस्त हो जाएंगे। यह उतना पागल नहीं है जितना कि यह लगता है - आपके पास आमतौर पर लक्ष्य के रूप में कुछ मेमोरी का उपयोग होता है, और सुनिश्चित करें कि आपका गेम उस सीमा तक नहीं पहुंचता है (उदाहरण के लिए, आरटीएस गेम में एक यूनिट काउंट लिमिट का उपयोग करके, या सीमित करके। एक एफपीएस के एक वर्ग में दुश्मनों की राशि)। यहां तक ​​कि अगर आप नहीं करते हैं, तो आप आमतौर पर किसी भी आधुनिक प्रणाली पर स्मृति से बाहर नहीं चलते हैं - आप वर्चुअल एड्रेस स्पेस (32-बिट एप्लिकेशन में) या स्टैक से बाहर निकलते हैं, उदाहरण के लिए। ओएस पहले से ही यह दिखावा करता है कि आपके पास जितनी मेमोरी है, उतने ही हैं - यह अमूर्त वर्चुअल मेमोरी प्रदान करता है। मुद्दा यह है, सिर्फ इसलिए कि आपके पास भौतिक रैम के 256 MiB का मतलब यह नहीं है कि आपका एप्लिकेशन मेमोरी के 4 GiB का उपयोग नहीं कर सकता है। कुछ खेलों में शायद इतना भी दिमाग न हो कि उनका सारा डेटा isn '


1

एक अपवाद को पकड़ना और जब और अपवाद उठाया जाता है, तो दो अलग-अलग चीजें होती हैं।

आपको स्मृति को मुक्त करने के लिए जटिल तर्क की आवश्यकता है जो आपके कार्यक्रम की आवश्यकता नहीं है। इससे भी बदतर, अगर कोई अन्य चल रहा प्रोग्राम या लाइब्रेरी मेमोरी लीक कर रहा है तो आपका इस पर कोई नियंत्रण नहीं है

केवल अच्छी बात यह है कि आप इसे शालीनतापूर्वक बंद करने के लिए कर सकते हैं और यही कि अधिकांश कार्यक्रमों को करने के लिए चुना जाएगा। आप तब तक प्रतीक्षा करने का निर्णय भी नहीं ले सकते जब तक कि स्मृति उपलब्ध नहीं होगी क्योंकि यह आपके कार्यक्रम को अनुत्तरदायी बना देगा।


अगर ओपी ने कहा कि अगर खेल वास्तव में "अपवादों का उपयोग नहीं करते हैं", तो वे एक को पकड़ नहीं सकते, न ही उठा सकते हैं, न ही प्रक्रिया कर सकते हैं। यदि अपवाद अक्षम हैं और कोई व्यक्ति एक को फेंकता है, तो प्रोग्राम को कॉल करने के बजाय std::terminate, और इसे करना होगा। लेकिन ऐसी शर्तों के तहत, आवंटन अपवाद को फेंकने के बजाय एक अशक्त सूचक लौटा सकता है, और जिसे अलग से संभाला जा सकता है।
अंडरस्कोर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.