जब वे अब संदर्भित नहीं होते हैं, तो Java ऑब्जेक्ट तुरंत हटाए नहीं जाते हैं?


77

जावा में, जैसे ही किसी वस्तु का अब कोई संदर्भ नहीं है, वह विलोपन के योग्य हो जाता है, लेकिन जेवीएम तब निर्णय लेता है जब वस्तु वास्तव में हटा दी जाती है। उद्देश्य-सी शब्दावली का उपयोग करने के लिए, सभी जावा संदर्भ स्वाभाविक रूप से "मजबूत" हैं। हालाँकि, Objective-C में, यदि किसी ऑब्जेक्ट में अब कोई मजबूत संदर्भ नहीं है, तो ऑब्जेक्ट को तुरंत हटा दिया जाता है। जावा में ऐसा क्यों नहीं है?


46
जब जावा ऑब्जेक्ट वास्तव में डिलीट हो जाते हैं तो आपको ध्यान नहीं देना चाहिए । यह एक कार्यान्वयन विवरण है।
बेसिल स्टारीनेविच

154
@BasileStarynkevitch आपको पूरी तरह से देखभाल और चुनौती देनी चाहिए कि आपका सिस्टम / प्लेटफॉर्म कैसे काम करता है। 'कैसे' और 'क्यों' सवाल पूछना बेहतर प्रोग्रामर बनने के लिए सबसे अच्छे तरीकों में से एक है (और, अधिक सामान्य अर्थों में, होशियार व्यक्ति)।
आर्टुर बियासीडोस्की

6
जब सर्कुलर रेफरेंस होते हैं तो ऑब्जेक्टिव C क्या करता है? मुझे लगता है कि यह सिर्फ उन्हें लीक करता है?
मेहरदाद

45
@ArturBiesiadowksi: नहीं, जावा विनिर्देश यह नहीं बताता है कि कोई वस्तु कब हटाई जाती है (और इसी तरह, R5RS के लिए )। आप अपने जावा प्रोग्राम को संभवतः विकसित कर सकते हैं जैसे कि यदि वह विलोपन कभी नहीं होता है (और जावा हेल्लो दुनिया की तरह अल्पकालिक प्रक्रियाओं के लिए, यह वास्तव में नहीं होता है)। आप जीवित वस्तुओं (या स्मृति की खपत) के सेट के बारे में परवाह कर सकते हैं, जो एक अलग कहानी है।
बेसिल स्टेयरनेविच

28
एक दिन नौसिखिए ने मास्टर से कहा "मेरे पास हमारी आवंटन समस्या का हल है। हम हर आवंटन को एक संदर्भ गणना देंगे, और जब यह शून्य तक पहुंच जाएगा, तो हम ऑब्जेक्ट को हटा सकते हैं"। कृति ने जवाब दिया "एक दिन नौसिखिए ने गुरु से कहा" मेरे पास एक उपाय है ...
एरिक लिपर्ट

जवाबों:


79

सबसे पहले, जावा में कमजोर संदर्भ हैं और एक और सर्वश्रेष्ठ-प्रयास श्रेणी जिसे नरम संदर्भ कहा जाता है। कमजोर बनाम मजबूत संदर्भ संदर्भ गिनती बनाम कचरा संग्रह से पूरी तरह से अलग मुद्दा है।

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

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


58
एक प्रोग्रामर की पत्नी उसे सुपरमार्केट भेजती है। वह उससे कहती है, "एक रोटी खरीदो और अगर तुम्हें कुछ अंडे दिखें तो एक दर्जन को पकड़ो।" प्रोग्रामर बाद में अपनी बांह के नीचे एक दर्जन रोटियों के साथ लौटता है।
नील

7
मेरा सुझाव है कि नई पीढ़ी का जीसी समय आमतौर पर जीवित वस्तुओं की मात्रा के समानुपाती होता है , इसलिए अधिक हटाई गई वस्तुओं का मतलब है कि उनकी लागत का भुगतान कई मामलों में बिल्कुल नहीं किया जाएगा। डिलीट सर्वाइवर स्पेस पॉइंटर को फ़्लिप करने जितना आसान है और वैकल्पिक रूप से पूरे मेमोरी स्पेस को एक बड़े मेमसेट में शून्य करना (यह सुनिश्चित नहीं है कि यह tcabs के आवंटन के दौरान किया जाता है या वर्तमान jvms में ऑब्जेक्ट्स को स्वयं संशोधित किया जाता है
Artur Biesiadowski

64
@ नहीं होना चाहिए कि 13 रोटियां?
JAD


13
@ जेएडी मैंने १३ कहा होगा, लेकिन अधिकांश इसे प्राप्त नहीं करते हैं। ;)
नील

86

क्योंकि ठीक से कुछ जानना अब संदर्भित नहीं है आसान नहीं है। आसान के करीब भी नहीं।

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


7
या आप एक पायथन दृष्टिकोण ले सकते हैं जहाँ आप जितना संभव हो उतने का उपयोग करते हैं, एक जीसी का सहारा लेते हुए जब आप उम्मीद करते हैं कि मेमोरी को लीक करने वाले परिपत्र निर्भरताएं हैं। मैं नहीं देखता कि वे जीसी के अलावा रिफंडिंग क्यों नहीं कर सकते थे?
मेहरदाद

27
@ मेहरदाद वे कर सकते थे। लेकिन शायद यह धीमा होगा। इसे लागू करने से कुछ भी नहीं रुकता है, लेकिन हॉटस्पॉट या ओपनजे 9 में किसी भी जीसी को हराने की उम्मीद न करें।
जोसेफ

21
@ jpmc26 क्योंकि यदि आप वस्तुओं को जल्द से जल्द उपयोग में नहीं लाते हैं, तो संभावना अधिक है कि आप उन्हें एक उच्च लोड स्थिति में हटा दें, जो लोड को और अधिक बढ़ा देती है। कम लोड होने पर जीसी चल सकता है। संदर्भ की गिनती ही हर संदर्भ के लिए एक छोटा उपरि है। जीसी के साथ आप अक्सर एकल ऑब्जेक्ट्स को हैंडल किए बिना मेमोरी के एक बड़े हिस्से को बिना किसी संदर्भ के छोड़ सकते हैं।
जोसेफ

33
@ जोसेफ: उचित संदर्भ गिनती या तो मुफ्त नहीं है; रेफरेंस काउंट अपडेट में परमाणु वृद्धि / गिरावट की आवश्यकता होती है, जो आश्चर्यजनक रूप से महंगी हैं , खासकर आधुनिक मल्टीकोर आर्किटेक्चर पर। CPython में यह बहुत अधिक समस्या नहीं है (CPython अपने आप में बेहद धीमा है, और GIL अपने बहु-प्रदर्शन को एकल-कोर स्तरों तक सीमित करता है), लेकिन एक तेज़ भाषा पर जो समानता का समर्थन भी करती है, यह एक समस्या हो सकती है। यह मौका नहीं है कि PyPy को पूरी तरह से संदर्भ गणना से छुटकारा मिल जाए और वह GC का उपयोग करता है।
माटेओ इटालिया

10
@ मेहरदाद एक बार जब आपने जावा के लिए अपने संदर्भ गिनती जीसी को लागू कर दिया है तो मैं ख़ुशी से इसे एक ऐसे मामले को खोजने के लिए परीक्षण करूँगा जहां यह किसी अन्य जीसी कार्यान्वयन से भी बदतर प्रदर्शन करता है।
जोसेफ

45

AFAIK, JVM विनिर्देशन (अंग्रेजी में लिखा गया) का उल्लेख नहीं है जब वास्तव में एक वस्तु (या एक मूल्य) को हटा दिया जाना चाहिए, और इसे लागू करने के लिए छोड़ देता है (इसी तरह R5RS के लिए )। इसे किसी तरह एक कचरा संग्रहकर्ता की आवश्यकता होती है या सुझाव देता है लेकिन कार्यान्वयन के लिए विवरण छोड़ देता है। और इसी तरह जावा विनिर्देशन के लिए।

याद रखें कि प्रोग्रामिंग भाषाओं हैं विनिर्देशों (की वाक्य रचना , अर्थ विज्ञान , नहीं सॉफ्टवेयर कार्यान्वयन, आदि ...)। जावा (या इसके जेवीएम) जैसी भाषा में कई कार्यान्वयन हैं। इसका विनिर्देश प्रकाशित , डाउनलोड करने योग्य है (इसलिए आप इसका अध्ययन कर सकते हैं) और अंग्रेजी में लिखा गया है। M2.5.3 JVM युक्ति के ढेर में कचरा बीनने वाले का उल्लेख है:

वस्तुओं के लिए ढेर भंडारण एक स्वचालित भंडारण प्रबंधन प्रणाली (कचरा कलेक्टर के रूप में जाना जाता है) द्वारा पुनः प्राप्त किया जाता है; वस्तुओं को स्पष्ट रूप से निपटाया नहीं जाता है। जावा वर्चुअल मशीन किसी विशेष प्रकार के स्वचालित भंडारण प्रबंधन प्रणाली को नहीं मानती है

(जोर मेरा है; जावा कल्पना के ization12.6 में बीटीडब्लू फाइनल का उल्लेख किया गया है और एक मेमोरी मॉडल जावा कल्पना के §17.4 में है)

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

मुझे लगता है कि कई JVM कार्यान्वयनों पर एक हेल्लो वर्ल्ड वन की तरह एक अल्पकालिक जावा प्रोग्राम चल रहा है, कचरा कलेक्टर को बिल्कुल भी ट्रिगर नहीं किया जाता है और कोई विलोपन नहीं होता है। AFAIU, इस तरह के व्यवहार कई जावा चश्मा के अनुरूप है।

अधिकांश JVM कार्यान्वयन का उपयोग पीढ़ीगत नकल तकनीक (सबसे जावा वस्तुओं के लिए कम से कम, का उपयोग नहीं कर उन को अंतिम रूप देने या कमजोर संदर्भ ;, और अंतिम रूप देने के कम समय में होने की गारंटी नहीं है और स्थगित किया जा सकता है तो बस एक उपयोगी सुविधा है कि आपके कोड नहीं करना चाहिए बहुत कुछ निर्भर करता है) जिसमें किसी व्यक्तिगत वस्तु को हटाने की धारणा का कोई मतलब नहीं होता है (चूंकि कई ऑब्जेक्ट्स के लिए मेमोरी-जोनिंग मेमोरी ज़ोन का एक बड़ा ब्लॉक-, शायद एक साथ कई मेगाबाइट्स, एक ही बार में रिलीज़ हो जाते हैं)।

यदि JVM विनिर्देशन के लिए आवश्यक है कि प्रत्येक वस्तु को यथाशीघ्र हटा दिया जाए (या बस वस्तु विघटन पर और अधिक अड़चनें डाल दी जाए), कुशल जेनेशनल GC तकनीक निषिद्ध होगी, और जावा और JVM के डिज़ाइनर इससे बचने में बुद्धिमान रहे हैं।

BTW, यह संभव हो सकता है कि एक भोली JVM जो कभी भी वस्तुओं को नहीं हटाता है और मेमोरी को रिलीज़ नहीं करता है वह ऐनक (पत्र, आत्मा नहीं) के अनुरूप हो सकता है और निश्चित रूप से अभ्यास में एक हैलो वर्ल्ड चीज़ को चलाने में सक्षम है (ध्यान दें कि सबसे छोटे और अल्पकालिक जावा प्रोग्राम शायद कुछ गिगाबाइट्स से अधिक मेमोरी आवंटित नहीं करते हैं)। बेशक इस तरह के एक जेवीएम उल्लेख के लायक नहीं है और सिर्फ एक खिलौना चीज है (जैसे कि सी के लिए यह कार्यान्वयन mallocहै)। अधिक के लिए एप्सिलॉन नूप जीसी देखें । वास्तविक जीवन JVM सॉफ्टवेयर के बहुत जटिल टुकड़े हैं और कई कचरा संग्रह तकनीकों को मिलाते हैं।

इसके अलावा, जावा जेवीएम के समान नहीं है, और आपके पास जावा कार्यान्वयन जेवीएम के बिना चल रहा है (जैसे कि आगे जावा कंपाइलर, एंड्रॉइड रनटाइम )। में कुछ मामलों (ज्यादातर शैक्षिक वाले), तो आप कल्पना कीजिए कि एक जावा प्रोग्राम का आवंटन या क्रम (जैसे पर नहीं हटाते हैं क्योंकि (ताकि "संकलन समय कचरा संग्रहण" तकनीक कहा जाता है) हो सकता है अनुकूलन संकलक काफी चतुर को केवल का उपयोग किया गया है कॉल स्टैक और स्वचालित चर )।

जब वे अब संदर्भित नहीं होते हैं, तो Java ऑब्जेक्ट तुरंत हटाए नहीं जाते हैं?

क्योंकि Java और JVM स्पेक्स की आवश्यकता नहीं है।


अधिक (और JVM कल्पना ) के लिए GC हैंडबुक पढ़ें । ध्यान दें कि किसी वस्तु के लिए जीवित (या भविष्य की गणना के लिए उपयोगी) एक संपूर्ण-कार्यक्रम (गैर-मॉड्यूलर) संपत्ति है।

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

नहीं है कोई जादुई शब्द प्रोग्रामिंग और प्रोग्रामिंग भाषा डिजाइन में (के बारे में पता होना लंगड़ा समस्या ; एक उपयोगी रहने वाले वस्तु जा रहा है अनिर्णनीय सामान्य रूप में)।

तुम भी पढ़ सकता है SICP , भाषा उपयोगितावाद प्रोग्रामिंग , ड्रैगन बुक , लिस्प छोटे टुकड़ों में और आपरेटिंग सिस्टम: तीन आसान मोहरे । वे जावा के बारे में नहीं हैं, लेकिन वे आपके दिमाग को खोलेंगे और यह समझने में मदद करेंगे कि एक जेवीएम को क्या करना चाहिए और यह आपके कंप्यूटर पर व्यावहारिक रूप से (अन्य टुकड़ों के साथ) कैसे काम कर सकता है। आप मौजूदा खुले स्रोत JVM कार्यान्वयन (जैसे OpenJDK , जिसमें कई लाखों स्रोत कोड लाइनें हैं) के जटिल स्रोत कोड का अध्ययन करने में कई महीने (या कई साल) खर्च कर सकते हैं ।


20
"यह संभव हो सकता है कि एक भोली जेवीएम जो वस्तुओं को कभी नहीं हटाता है और स्मृति को रिलीज नहीं करता है वह चश्मा के अनुरूप हो सकता है" यह निश्चित रूप से कल्पना के अनुरूप है! जावा 11 वास्तव में , अन्य चीजों के अलावा, बहुत ही अल्पकालिक कार्यक्रमों के लिए एक नो-ऑप कचरा कलेक्टर को जोड़ रहा है।
माइकल

6
"आपको ध्यान नहीं देना चाहिए जब कोई वस्तु नष्ट हो जाती है" असहमत। एक के लिए, आपको पता होना चाहिए कि RAII अब संभव नहीं है और आप finalizeकिसी भी संसाधन प्रबंधन (फ़ाइलहैंडल, db कनेक्शन, gpu संसाधन, आदि) के लिए निर्भर नहीं रह सकते ।
अलेक्जेंडर

4
@ मिचेल यह एक प्रयुक्त मेमोरी छत के साथ बैच प्रसंस्करण के लिए एकदम सही समझ में आता है। ओएस बस कह सकता है "इस कार्यक्रम द्वारा उपयोग की जाने वाली सभी मेमोरी अब चली गई है!" सब के बाद, जो तेजी से नहीं है। दरअसल, सी में कई कार्यक्रम उस तरह से लिखे गए थे, खासकर शुरुआती यूनिक्स दुनिया में। पास्कल के पास खूबसूरती से भयानक "पूर्व-सहेजे गए चेकपॉइंट के लिए स्टैक / हीप पॉइंटर को रीसेट करना" था जिसने आपको बहुत ही समान काम करने की अनुमति दी, हालांकि यह काफी असुरक्षित था - उप-कार्य, रीसेट शुरू करें।
लुआं

6
C ++ के बाहर सामान्य रूप से #Alexander (और कुछ भाषाएं जो जानबूझकर इससे प्राप्त होती हैं), यह मानते हुए कि RAII फाइनली के आधार पर काम करेगी अकेले एक प्रतिमान है, जिसके खिलाफ चेतावनी दी जानी चाहिए और एक स्पष्ट संसाधन नियंत्रण ब्लॉक के साथ प्रतिस्थापित किया जाना चाहिए। जीसी की पूरी बात यह है कि जीवनकाल और संसाधन सभी के बाद, विघटित हो जाते हैं।
Leushenko

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

23

उद्देश्य-सी शब्दावली का उपयोग करने के लिए, सभी जावा संदर्भ स्वाभाविक रूप से "मजबूत" हैं।

यह सही नहीं है - जावा में कमजोर और नरम दोनों संदर्भ हैं, हालांकि ये भाषा के कीवर्ड के बजाय ऑब्जेक्ट स्तर पर लागू होते हैं।

ऑब्जेक्टिव-सी में, यदि किसी ऑब्जेक्ट में अब कोई मजबूत संदर्भ नहीं है, तो ऑब्जेक्ट तुरंत हटा दिया जाता है।

यह भी आवश्यक रूप से सही नहीं है - उद्देश्य सी के कुछ संस्करणों ने वास्तव में एक पीढ़ीगत कचरा संग्रहकर्ता का उपयोग किया। अन्य संस्करणों में कोई कचरा संग्रह नहीं था।

यह सही है कि ऑब्जेक्टिव C के नए संस्करण ट्रेस आधारित GC की बजाय ऑटोमैटिक रेफरेंस काउंटिंग (ARC) का उपयोग करते हैं, और यह (अक्सर) उस रिजल्ट को "डिलीट" किए जाने के परिणामस्वरूप होता है, जब वह रेफरेंस काउंट शून्य से हिट होता है। हालांकि, ध्यान दें कि एक जेवीएम कार्यान्वयन भी अनुपालन हो सकता है और ठीक इसी तरह से काम कर सकता है (बिल्ली, यह आज्ञाकारी हो सकता है और इसमें कोई जीसी नहीं है।)

तो क्यों ज्यादातर JVM कार्यान्वयन ऐसा नहीं करते हैं, और इसके बजाय ट्रेस आधारित GC एल्गोरिदम का उपयोग करते हैं?

सीधे शब्दों में कहें, एआरसी यूटोपियन नहीं है क्योंकि यह पहली बार लगता है:

  • आपको हर बार एक संदर्भ की प्रतिलिपि, संशोधित, या दायरे से बाहर जाने पर काउंटर को बढ़ाना या घटाना पड़ता है, जो एक स्पष्ट प्रदर्शन ओवरहेड लाता है।
  • एआरसी आसानी से चक्रीय संदर्भों को स्पष्ट नहीं कर सकता है, क्योंकि वे सभी एक दूसरे के लिए संदर्भ हैं, इस प्रकार उनकी संदर्भ गणना कभी शून्य नहीं होती है।

एआरसी के पाठ्यक्रम के फायदे हैं - इसे लागू करना और संग्रह करना सरल है। लेकिन ऊपर के नुकसान, दूसरों के बीच, जेवीएम कार्यान्वयन के अधिकांश कारण एक पीढ़ीगत, ट्रेस आधारित जीसी का उपयोग करेंगे।


1
मजेदार बात यह है कि Apple ने एआरसी को ठीक से स्विच किया क्योंकि उन्होंने देखा कि व्यवहार में, यह अन्य जीसी (विशेष रूप से जनरलों में) को बहुत बेहतर ढंग से बेहतर बनाता है। निष्पक्ष होने के लिए, यह ज्यादातर मेमोरी-विवश प्लेटफार्मों (iPhone) पर सच है। लेकिन मैं आपके कथन का जवाब दूंगा कि "ARC पहले जैसा प्रतीत नहीं होता है" जैसा कि यह कहकर कि "जेनरिक (और अन्य गैर-नियतात्मक) GCs उतने नहीं हैं जितने कि वे पहले लगते हैं: नियतात्मक विनाश शायद एक बेहतर विकल्प है अधिकांश परिदृश्य।
कोनराड रुडोल्फ

3
@KonradRudolph यद्यपि मैं निर्धारक विनाश का भी प्रशंसक हूं, लेकिन मुझे नहीं लगता कि "अधिकांश परिदृश्यों में बेहतर विकल्प" है। यह निश्चित रूप से एक बेहतर विकल्प है जब विलंबता या स्मृति औसत थ्रूपुट से अधिक महत्वपूर्ण है, और विशेष रूप से जब तर्क यथोचित सरल होता है। लेकिन ऐसा नहीं है कि कई जटिल अनुप्रयोग नहीं हैं जिनमें बहुत सारे चक्रीय संदर्भ आदि की आवश्यकता होती है और तेज औसत संचालन की आवश्यकता होती है, लेकिन वास्तव में विलंबता के बारे में परवाह नहीं है और बहुत सारी स्मृति उपलब्ध है। इनके लिए, एआरसी एक अच्छा विचार है, तो यह संदिग्ध है।
लेफ्टनैबाउटआउट

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

3
@KonradRudolph यदि ARC को प्रोग्रामर द्वारा कुछ सरल मैनुअल हस्तक्षेप की आवश्यकता होती है, तो यह चक्रों से नहीं निपटता है। यदि आप दोगुनी-लिंक्ड सूचियों का भारी उपयोग करना शुरू करते हैं, तो एआरसी मैन्युअल मेमोरी आवंटन में भटक जाती है। यदि आपके पास बड़े पैमाने पर मनमाने ढंग से रेखांकन हैं, तो एआरसी आपको एक कचरा कलेक्टर लिखने के लिए मजबूर करता है। जीसी तर्क यह होगा कि जिन संसाधनों को विनाश की आवश्यकता होती है, वे मेमोरी सबसिस्टम का काम नहीं हैं, और उनमें से कुछ को ट्रैक करने के लिए, उन्हें प्रोग्रामर द्वारा कुछ सरल मैनुअल हस्तक्षेप के माध्यम से नियत रूप से अंतिम रूप दिया जाना चाहिए।
prosfilaes

2
@KonradRudolph ARC और साइकिल मूल रूप से मेमोरी लीक की ओर जाता है यदि वे मैन्युअल रूप से नियंत्रित नहीं होते हैं। जटिल पर्याप्त प्रणालियों में, बड़ी लीक तब हो सकती है जैसे कि किसी मानचित्र में संग्रहित कोई वस्तु उस मानचित्र के संदर्भ को संग्रहीत करती है, एक परिवर्तन जो प्रोग्रामर द्वारा बनाया जा सकता है, कोड के अनुभागों के प्रभारी नहीं है और उस मानचित्र को नष्ट कर सकता है। बड़े मनमाने रेखांकन का मतलब यह नहीं है कि आंतरिक संकेत मजबूत नहीं हैं, कि यह गायब होने से जुड़ी वस्तुओं के लिए ठीक है। चाहे कुछ मेमोरी लीक से निपटने के लिए मैन्युअल रूप से फ़ाइलों को बंद करने की तुलना में कोई समस्या नहीं है, मैं नहीं कहूंगा, लेकिन यह वास्तविक है।
अभियोजन पक्ष

5

जावा ठीक से निर्दिष्ट नहीं करता है जब वस्तु एकत्र हो जाती है क्योंकि इससे कार्यान्वयन को यह चुनने की स्वतंत्रता मिलती है कि कचरा संग्रह कैसे संभालना है।

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

मल्टीथ्रेडेड कोड में, लागत अधिक होती है। यह या तो परमाणु वेतन वृद्धि / गिरावट या ताले के लिए कहता है, जो दोनों महंगे हो सकते हैं। एक आधुनिक प्रोसेसर पर, एक परमाणु ऑपरेशन एक साधारण रजिस्टर ऑपरेशन की तुलना में 20x अधिक महंगा के आदेश पर हो सकता है (जाहिर है कि प्रोसेसर से प्रोसेसर में भिन्न होता है)। इससे लागत बढ़ सकती है।

तो इसके साथ, हम कई मॉडलों द्वारा किए गए ट्रेडऑफ़ पर विचार कर सकते हैं।

  • उद्देश्य-सी एआरसी पर केंद्रित है - स्वचालित संदर्भ गिनती। उनका दृष्टिकोण सब कुछ के लिए संदर्भ गिनती का उपयोग करना है। कोई चक्र पहचान नहीं है (जो मुझे पता है), इसलिए प्रोग्रामर से साइकिल को होने से रोकने की अपेक्षा की जाती है, जिससे विकास का समय लगता है। उनका सिद्धांत यह है कि पॉइंटर्स को वह सब अक्सर नहीं सौंपा जाता है, और उनका कंपाइलर उन स्थितियों की पहचान कर सकता है, जहां रेफ़रिंग / डिक्रीफ़ॉर्मिंग रेफरेंस काउंट्स ऑब्जेक्ट को मरने का कारण नहीं बना सकते हैं, और उन इंक्रीमेंट्स / डीक्रेट्स को पूरी तरह से खत्म कर सकते हैं। इस प्रकार वे संदर्भ गिनती की लागत को कम करते हैं।

  • सीपीथॉन एक संकर तंत्र का उपयोग करता है। वे संदर्भ गणना का उपयोग करते हैं, लेकिन उनके पास एक कचरा कलेक्टर भी है जो चक्रों की पहचान करता है और उन्हें जारी करता है। यह दोनों दृष्टिकोणों की कीमत पर, दोनों दुनिया के लाभ प्रदान करता है। CPython को संदर्भ मायने रखता है औरपुस्तक चक्र का पता लगाने के लिए करते हैं। सीपीथॉन दो तरीकों से इससे दूर हो जाता है। मुट्ठी यह है कि सीपीथॉन वास्तव में पूरी तरह से बहुआयामी नहीं है। इसमें GIL के रूप में जाना जाने वाला एक लॉक है जो मल्टीथ्रेडिंग को सीमित करता है। इसका अर्थ है कि सीपीथॉन परमाणु वाले के बजाय सामान्य वेतन वृद्धि / गिरावट का उपयोग कर सकता है, जो कि बहुत तेज है। सीपीथॉन की व्याख्या भी की जाती है, जिसका अर्थ है कि एक चर के लिए असाइनमेंट जैसे संचालन पहले से ही केवल 1 के बजाय निर्देश का एक मुट्ठी भर लेते हैं। वेतन वृद्धि / डिक्रीमेंट करने की अतिरिक्त लागत, जो सी कोड में जल्दी से किया जाता है, एक समस्या से कम है 'हम' ve पहले से ही इस लागत का भुगतान किया।

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

इसलिए हम देख सकते हैं कि इन तीनों में से प्रत्येक को ट्रेडऑफ बनाना था। कौन सा ट्रेडऑफ सबसे अच्छा है यह इस बात पर निर्भर करता है कि भाषा का उपयोग कैसे किया जाता है।


4

हालांकि finalizeजावा के GC पर पिगी-समर्थित था, इसके मूल में कचरा संग्रह मृत वस्तुओं में दिलचस्पी नहीं रखता है, लेकिन जीवित हैं। कुछ जीसी सिस्टम (संभवतः जावा के कुछ कार्यान्वयन सहित) पर, केवल एक चीज बिट्स के एक समूह को भेद करती है जो किसी ऑब्जेक्ट को स्टोरेज के एक समूह से दर्शाती है जो किसी भी चीज़ के लिए उपयोग नहीं किया जाता है जो पूर्व के संदर्भों का अस्तित्व हो सकता है। जबकि फाइनल के साथ वस्तुओं को एक विशेष सूची में जोड़ा जाता है, अन्य वस्तुओं में ब्रह्मांड में कहीं भी कुछ भी नहीं हो सकता है जो कहता है कि उनका भंडारण उपयोगकर्ता कोड में रखे गए संदर्भों को छोड़कर एक वस्तु से जुड़ा हुआ है। जब इस तरह के अंतिम संदर्भ को ओवरराइट किया जाता है, तो स्मृति में बिट पैटर्न तुरंत एक वस्तु के रूप में पहचानने योग्य हो जाएगा, ब्रह्मांड में कुछ भी पता नहीं है या नहीं।

कचरा संग्रहण का उद्देश्य उन वस्तुओं को नष्ट करना नहीं है जिनके लिए कोई संदर्भ मौजूद नहीं है, बल्कि तीन चीजों को पूरा करने के लिए:

  1. अमान्य कमजोर संदर्भ जो उन वस्तुओं की पहचान करते हैं जिनके पास कोई भी दृढ़ता से उपलब्ध संदर्भ नहीं है।

  2. सिस्टम की अंतिम सूची के साथ वस्तुओं की सूची को देखें कि क्या उनमें से कोई भी उनके साथ जुड़ा हुआ कोई ठोस संदर्भ नहीं है।

  3. किसी ऑब्जेक्ट द्वारा उपयोग किए जा रहे संग्रहण के क्षेत्रों को पहचानें और समेकित करें।

ध्यान दें कि GC का प्राथमिक लक्ष्य # 3 है, और इसे करने से पहले जितना लंबा इंतजार करना होगा, समेकन के अधिक अवसर एक होने की संभावना है। यह उन मामलों में # 3 करने के लिए समझ में आता है जहां किसी के पास भंडारण के लिए तत्काल उपयोग होगा, लेकिन अन्यथा यह इसे स्थगित करने के लिए अधिक समझ में आता है।


5
दरअसल, gc का केवल एक ही लक्ष्य होता है: अनंत स्मृति का अनुकरण करना। एक लक्ष्य के रूप में आपके द्वारा नामित सब कुछ या तो अमूर्तता में एक अपूर्णता है या कार्यान्वयन-विस्तार है।
Deduplicator

@Deduplicator: कमजोर संदर्भ उपयोगी शब्दार्थ प्रदान करते हैं जिन्हें GC सहायता के बिना प्राप्त नहीं किया जा सकता है।
सुपरकैट

यकीन है, कमजोर संदर्भों में उपयोगी शब्दार्थ हैं। लेकिन अगर अनुकरण बेहतर होता तो क्या उन शब्दार्थों की जरूरत होती?
Deduplicator

@ चिकित्सक: हाँ। एक संग्रह पर विचार करें जो परिभाषित करता है कि कैसे गणना के साथ अपडेट होगा। इस तरह के संग्रह में किसी भी जीवित प्रगणकों के लिए कमजोर संदर्भ रखने की आवश्यकता हो सकती है। असीमित-मेमोरी सिस्टम में, एक संग्रह जिसे बार-बार पुनरावृत्त किया गया था, उसकी रुचि के प्रगणकों की सूची बिना बँधे बढ़ती जाएगी। उस सूची के लिए आवश्यक मेमोरी एक समस्या नहीं होगी, लेकिन इसके माध्यम से पुनरावृति करने के लिए आवश्यक समय सिस्टम के प्रदर्शन को खराब कर देगा। जीसी को जोड़ने का मतलब ओ (एन) और ओ (एन ^ 2) एल्गोरिदम के बीच अंतर हो सकता है।
सुपरकैट

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

4

मुझे आपके प्रश्न का पुनः प्रकाशन और सामान्यीकरण सुझाने दें:

जावा अपनी GC प्रक्रिया के बारे में मजबूत गारंटी क्यों नहीं देता है?

इसे ध्यान में रखते हुए, यहां उत्तरों के माध्यम से त्वरित स्क्रॉल करें। अभी तक सात (इस एक की गिनती नहीं) कर रहे हैं, काफी कुछ टिप्पणी धागे के साथ।

वही तुम्हारा उत्तर है।

जीसी कठिन है। बहुत सारे विचार हैं, विभिन्न ट्रेडऑफ़ के बहुत सारे, और, अंततः, बहुत अलग दृष्टिकोण हैं। उन तरीकों में से कुछ इसे जीसी के लिए एक वस्तु के रूप में जल्द से जल्द संभव बनाते हैं; दूसरों को नहीं। अनुबंध को ढीला रखकर, जावा अपने कार्यान्वयनकर्ताओं को अधिक विकल्प देता है।

उस निर्णय में भी एक ट्रेडऑफ है, निश्चित रूप से: अनुबंध को ढीला रखने से, जावा ज्यादातर * प्रोग्रामर के लिए विनाशकर्ताओं पर भरोसा करने की क्षमता को छीन लेता है। यह कुछ ऐसा है जो सी ++ प्रोग्रामर विशेष रूप से अक्सर याद करते हैं ([उद्धरण वांछित];)), इसलिए यह एक तुच्छ व्यापार नहीं है। मैंने उस विशेष मेटा-निर्णय की चर्चा नहीं देखी है, लेकिन संभवतः जावा के लोगों ने फैसला किया कि अधिक जीसी विकल्प होने के लाभों ने प्रोग्रामर को वास्तव में बताने में सक्षम होने के लाभों से आगे निकल गए जब कोई वस्तु नष्ट हो जाएगी।


* finalizeविधि है, लेकिन विभिन्न कारणों से जो इस उत्तर के दायरे से बाहर हैं, यह कठिन है और इस पर भरोसा करना अच्छा नहीं है।


3

डेवलपर द्वारा लिखित स्पष्ट कोड के बिना मेमोरी को संभालने की दो अलग-अलग रणनीतियां हैं: कचरा संग्रह, और संदर्भ गिनती।

कचरा संग्रह का लाभ यह है कि यह "काम करता है" जब तक कि डेवलपर कुछ बेवकूफ न करे। संदर्भ गिनती के साथ, आपके पास संदर्भ चक्र हो सकते हैं, जिसका अर्थ है कि यह "काम करता है" लेकिन डेवलपर को कभी-कभी चतुर होना पड़ता है। तो यह कचरा संग्रहण के लिए एक प्लस है।

संदर्भ की गिनती के साथ, वस्तु तुरंत चली जाती है जब संदर्भ गणना शून्य तक जाती है। यह संदर्भ गिनती के लिए एक फायदा है।

यदि आप संदर्भ गणना के प्रशंसकों को मानते हैं तो स्पीडवाइज़, कचरा संग्रह तेज़ है, यदि आप कचरा संग्रह के प्रशंसकों को मानते हैं, और संदर्भ की गिनती तेज़ है।

यह एक ही लक्ष्य को प्राप्त करने के लिए सिर्फ दो अलग-अलग तरीके हैं, जावा ने एक विधि को अपनाया, ऑब्जेक्टिव-सी ने एक और उठाया (और इसे एक दर्द-इन-गधे से कुछ के लिए बदलने के लिए संकलक समर्थन जोड़ा गया जो डेवलपर्स के लिए बहुत कम काम है)।

कचरा संग्रहण से संदर्भ गिनती तक जावा को बदलना एक बड़ा उपक्रम होगा, क्योंकि बहुत सारे कोड परिवर्तन की आवश्यकता होगी।

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

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


संदर्भ-गणना के साथ, अंतिम रूप तुच्छ है, क्योंकि प्रोग्रामर ने चक्र का ध्यान रखा। जीसी के साथ, चक्र तुच्छ हैं, लेकिन प्रोग्रामर को अंतिम रूप देने के साथ सावधान रहना होगा।
Deduplicator

@Deduplicator जावा में, अंतिम रूप दी जा रही वस्तुओं के लिए मजबूत संदर्भ बनाना भी संभव है ... ऑब्जेक्टिव-सी और स्विफ्ट में, एक बार संदर्भ संख्या शून्य होने पर, ऑब्जेक्ट गायब हो जाएगा (जब तक कि आप डीललॉक / डिनिस्ट में अनंत लूप नहीं डालते हैं)।
gnasher729

बस बेवकूफ़ वर्तनी की जाँच करने वाले चेकर की जगह
हिना के

1
एक कारण है कि अधिकांश प्रोग्रामर स्वचालित वर्तनी सुधार से घृणा करते हैं ... ;-)
Deduplicator

योग्य ... मुझे लगता है कि दुनिया को उन लोगों के बीच 0.1 / 0.1 / 99.8 में विभाजित किया गया है, जो सोचते हैं कि कचरा संग्रह में संदर्भ गिनती जोड़ना एक बुरा विचार है, और जो लोग सोचते हैं कि संदर्भ गणना में कचरा संग्रह जोड़ना एक बुरा विचार है, और जो लोग कचरा संग्रहण के आने तक दिनों की गिनती करते रहें क्योंकि वह टन पहले से ही बदबूदार हो रहा है ...
वामावर्त

1

अन्य सभी निष्पादन तर्क और समझने की कठिनाई के बारे में चर्चा जब किसी वस्तु के संदर्भ नहीं होते हैं तो सही होते हैं, हालांकि एक अन्य विचार जो मुझे लगता है कि ध्यान देने योग्य है, कम से कम एक जेवीएम (एजुल) है जो इस तरह से कुछ समझता है इसमें वह समानांतर gc को लागू करता है जो अनिवार्य रूप से एक vm थ्रेड होता है जो उन्हें हटाने के प्रयास के लिए लगातार संदर्भों की जाँच करता है जो कि आप जिस बारे में बात कर रहे हैं उससे पूरी तरह से असमान रूप से कार्य नहीं करेंगे। मूल रूप से यह लगातार ढेर पर चारों ओर देखेगा और किसी भी मेमोरी को पुनः प्राप्त करने का प्रयास करेगा जिसे संदर्भित नहीं किया जा रहा है। यह बहुत मामूली प्रदर्शन लागत को प्रभावित करता है लेकिन यह अनिवार्य रूप से शून्य या बहुत कम जीसी समय की ओर जाता है। (यह है कि जब तक कि लगातार विस्तार करने वाला ढेर का आकार सिस्टम रैम से अधिक नहीं हो जाता है और तब अज़ुल भ्रमित हो जाता है और फिर ड्रेगन हो जाते हैं)

TLDR कुछ इस तरह की तरह JVM के लिए मौजूद है यह सिर्फ एक विशेष jvm है और इसमें किसी भी अन्य इंजीनियरिंग समझौते की तरह कमियां हैं।

डिस्क्लेमर: अज़ुल से मेरा कोई नाता नहीं है, हमने इसे पिछली नौकरी में इस्तेमाल किया था।


1

निरंतर थ्रूपुट को अधिकतम करना या जीसी विलंबता को कम करना गतिशील तनाव में है, जो संभवतः सबसे सामान्य कारण है कि जीसी तुरंत नहीं होता है। कुछ सिस्टम में, 911 आपातकालीन ऐप की तरह, एक विशिष्ट विलंबता सीमा को पूरा नहीं करने से साइट विफल होने वाली प्रक्रियाओं को ट्रिगर करना शुरू कर सकती है। दूसरों में, बैंकिंग और / या मध्यस्थता साइट की तरह, यह थ्रूपुट को अधिकतम करने के लिए कहीं अधिक महत्वपूर्ण है।


0

गति

यह सब क्यों चल रहा है अंततः गति की वजह से। यदि प्रोसेसर असीम रूप से तेज थे, या इसके करीब (व्यावहारिक होने के लिए), उदाहरण के लिए, 1,000,000,000,000,000,000,000,000,000,000,000,000 संचालन प्रति सेकंड तो आप प्रत्येक ऑपरेटर के बीच लंबे समय तक और जटिल चीजें हो सकती हैं, जैसे कि सुनिश्चित करें कि डी-रेफर किए गए ऑब्जेक्ट हटा दिए गए हैं। चूंकि प्रति सेकंड ऑपरेशन की संख्या वर्तमान में सही नहीं है और, जैसा कि अधिकांश अन्य उत्तर बताते हैं कि यह वास्तव में जटिल है और यह पता लगाने के लिए संसाधन गहन है, कचरा संग्रह मौजूद है ताकि प्रोग्राम उन पर ध्यान केंद्रित कर सकें जो वे वास्तव में प्राप्त करने की कोशिश कर रहे हैं। शीघ्रता से।


खैर, मुझे यकीन है कि हम अतिरिक्त चक्रों का उपयोग करने के लिए और अधिक दिलचस्प तरीके ढूंढेंगे।
डिडुप्लिकेटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.