जावा का स्ट्रिंग निरंतर पूल कहाँ रहता है, ढेर या स्टैक?


104

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


11
स्टैक पर एक पूल कैसे संग्रहीत किया जा सकता है? क्या आपको स्टैक की अवधारणा पता है?
द स्क्रैम मिस्टर

1
हाय स्क्रैम मिस्टर, मैंने इसका मतलब यह करने की कोशिश की कि यह नहीं हो सकता। गलत सम्मेलन के लिए क्षमा करें। जीसी के बारे में अभी-अभी मुझे पता चला। उसके लिए धन्यवाद
रेंगसामी रामानुजम

@ TheScrumMeister - वास्तव में, कुछ परिस्थितियों में वे कचरा एकत्र कर सकते हैं । "डील ब्रेकर" यह है कि किसी भी वर्ग के लिए कोड ऑब्जेक्ट जिसमें एक स्ट्रिंग शाब्दिक का उल्लेख है, वह स्ट्रिंग ऑब्जेक्ट के लिए एक संदर्भ होगा जो शाब्दिक का प्रतिनिधित्व करता है।
स्टीफन सी

जवाबों:


74

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


8
वास्तव में, जब वीएम में कक्षाएं भरी जाती हैं, तो स्ट्रिंग स्थिरांक ढेर में कॉपी हो जाएंगे, एक वीएम-वाइड स्ट्रिंग पूल (परमिटेन में, जैसा कि स्टीफन सी ने कहा था), क्योंकि विभिन्न वर्गों में बराबर स्ट्रिंग शाब्दिक होना चाहिए। वही स्ट्रिंग ऑब्जेक्ट (JLS द्वारा)।
पाओलो एबरमैन

1
आपके जवाबों के लिए आप सभी का धन्यवाद। मुझे इस चर्चा से बहुत कुछ समझ में आया। आप लोगों को यह जानकर अच्छा लगा :)
Rengasami Ramanujam

4
पाओलो, यह सूर्य की आभासी मशीन के लिए सच है, लेकिन जरूरी नहीं कि जेवीएम के सभी कार्यान्वयनों के लिए। जैसा कि जेवीएम कल्पना का उल्लेख है, हालांकि रनटाइम निरंतर पूल और विधि क्षेत्र तार्किक रूप से ढेर का हिस्सा हैं, उनके पास समान व्यवहार नहीं है। बस एक मामूली अर्थगत अंतर, वास्तव में :)
ड्यूने मूर


54

जैसा कि इस उत्तर द्वारा समझाया गया है , स्ट्रिंग पूल का सटीक स्थान निर्दिष्ट नहीं है और एक जेवीएम कार्यान्वयन से दूसरे में भिन्न हो सकता है।

यह ध्यान रखना दिलचस्प है कि जावा 7 तक, पूल हॉटस्पॉट JVM पर ढेर के परमेजन स्थान पर था, लेकिन यह जावा 7 के बाद से हीप के मुख्य भाग में चला गया है :

क्षेत्र : हॉटस्पॉट
सिनोप्सिस : JDK 7 में, आंतरिक स्ट्रिंग्स को जावा हीप की स्थायी पीढ़ी में आवंटित नहीं किया जाता है, बल्कि उन्हें जावा हीप के मुख्य भाग (युवा और पुरानी पीढ़ियों के रूप में जाना जाता है) में आवंटित किया जाता है, अन्य के साथ आवेदन द्वारा बनाई गई वस्तुओं। इस परिवर्तन के परिणामस्वरूप मुख्य जावा हीप में अधिक डेटा रहता है, और स्थायी पीढ़ी में कम डेटा, और इस प्रकार ढेर आकार को समायोजित करने की आवश्यकता हो सकती है। अधिकांश अनुप्रयोग इस परिवर्तन के कारण ढेर उपयोग में केवल अपेक्षाकृत छोटे अंतर देखेंगे, लेकिन कई अनुप्रयोग जो कई कक्षाओं को लोड करते हैं या String.intern () विधि का भारी उपयोग करते हैं, वे अधिक महत्वपूर्ण अंतर देखेंगे। RFE: 6962931

और जावा 8 हॉटस्पॉट में, स्थायी पीढ़ी पूरी तरह से हटा दी गई है।


30

स्ट्रिंग पर स्ट्रिंग के शाब्दिक संग्रहित नहीं हैं। कभी नहीँ। वास्तव में, स्टैक पर कोई ऑब्जेक्ट संग्रहीत नहीं किया जाता है।

स्ट्रिंग शाब्दिक (या अधिक सही, स्ट्रिंग वस्तुओं है कि उन्हें प्रतिनिधित्व करते हैं) कर रहे हैं ऐतिहासिक रूप से एक ढेर में जमा हो रहे थे "permgen" ढेर कहा जाता है। (स्थायी पीढ़ी के लिए परमिटेन छोटा है।)

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

CLARIFICATION # 1 - मैं यह नहीं कह रहा हूँ कि Permgen को GC'ed नहीं मिलता है। यह तब होता है, आमतौर पर जब JVM एक पूर्ण GC चलाने का निर्णय लेता है। मेरा कहना यह है कि स्ट्रिंग शाब्दिक तब तक पहुंच योग्य होगा जब तक कि उनका उपयोग करने वाला कोड पहुंच योग्य न हो, और कोड तब तक पहुंच योग्य रहेगा जब तक कोड का लोडर लोड करने योग्य हो, और डिफ़ॉल्ट क्लास लोडर के लिए, जिसका अर्थ है "हमेशा के लिए"।

क्लैरिएशन # 2 - वास्तव में, जावा 7 और बाद में स्ट्रिंग पूल को पकड़ने के लिए नियमित ढेर का उपयोग करता है। इस प्रकार, स्ट्रिंग ऑब्जेक्ट्स जो स्ट्रिंग शाब्दिक और इंटरनल स्ट्रिंग्स का प्रतिनिधित्व करते हैं, वास्तव में नियमित रूप से ढेर में होते हैं। (विवरण के लिए @ assylias का उत्तर देखें।)


लेकिन मैं अभी भी स्ट्रिंग शाब्दिक और स्ट्रिंग के भंडारण के बीच पतली रेखा का पता लगाने की कोशिश कर रहा हूं new

कोई "पतली रेखा" नहीं है। यह वास्तव में बहुत सरल है:

  • String स्ट्रिंग पूल में स्ट्रिंग शाब्दिक का प्रतिनिधित्व / अनुरूप करने वाली वस्तुएं आयोजित की जाती हैं।
  • Stringएक String::internकॉल द्वारा बनाई गई वस्तुओं को स्ट्रिंग पूल में आयोजित किया जाता है।
  • अन्य सभी Stringऑब्जेक्ट स्ट्रिंग पूल में नहीं रखे गए हैं।

फिर अलग सवाल है कि स्ट्रिंग पूल "संग्रहीत" कहां है। जावा 7 से पहले यह परमेजन ढेर था। जावा 7 के बाद से यह मुख्य ढेर है।


23

स्ट्रिंग पूलिंग

स्ट्रिंग पूलिंग (कभी-कभी इसे स्ट्रिंग कैनोनिकलिसेशन भी कहा जाता है) कई स्ट्रिंग ऑब्जेक्ट्स को समान मूल्य के साथ एक ही साझा स्ट्रिंग ऑब्जेक्ट के साथ अलग पहचान देने की एक प्रक्रिया है। आप अपने स्वयं के मानचित्र (संभवतः अपनी आवश्यकताओं के आधार पर नरम या कमजोर संदर्भों के साथ) और मानचित्र के मूल्यों को विहित मूल्यों के रूप में रखकर इस लक्ष्य को प्राप्त कर सकते हैं। या आप String.intern () पद्धति का उपयोग कर सकते हैं जो आपको JDK द्वारा प्रदान की गई है।

जावा 6 के समय String.intern () का उपयोग करते हुए कई मानकों द्वारा मना कर दिया गया था, अगर पूलिंग नियंत्रण से बाहर हो गई तो एक आउटऑफ़मैरीएक्स अपवाद प्राप्त करने की उच्च संभावना के कारण कई मानकों द्वारा मना किया गया था। स्ट्रिंग पूलिंग के ओरेकल जावा 7 कार्यान्वयन को काफी बदल दिया गया था। आप http://bugs.sun.com/view_bug.do?bug_id=6962931 और http://bugs.sun.com/view_bug.do?bug_id=6962930 पर विवरण देख सकते हैं ।

जावा 6 में String.intern ()

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

जावा 6 में ऐसे स्ट्रिंग पूल के साथ सबसे बड़ा मुद्दा इसका स्थान था - पर्मगेन। PermGen का एक निश्चित आकार है और इसे रनटाइम में विस्तारित नहीं किया जा सकता है। आप इसे -XX: MaxPermSize = 96m विकल्प का उपयोग करके सेट कर सकते हैं। जहाँ तक मुझे पता है, मंच के आधार पर डिफ़ॉल्ट पर्मेन का आकार 32M और 96M के बीच भिन्न होता है। आप इसका आकार बढ़ा सकते हैं, लेकिन इसका आकार अभी भी तय किया जाएगा। इस सीमा को String.intern के बहुत सावधानी से उपयोग की आवश्यकता है - आप इस पद्धति का उपयोग करके किसी भी अनियंत्रित उपयोगकर्ता इनपुट को बेहतर नहीं करेंगे। यही कारण है कि जावा 6 के समय में स्ट्रिंग पूलिंग ज्यादातर मैन्युअल रूप से प्रबंधित नक्शे में लागू की गई थी।

String.intern () जावा 7 में

ओरेकल इंजीनियरों ने जावा 7 में स्ट्रिंग पूलिंग लॉजिक के लिए एक बहुत ही महत्वपूर्ण बदलाव किया - स्ट्रिंग पूल को ढेर में स्थानांतरित कर दिया गया। इसका मतलब है कि आप अब एक अलग निश्चित आकार के मेमोरी क्षेत्र द्वारा सीमित नहीं हैं। सभी स्ट्रिंग्स अब ढेर में स्थित हैं, अधिकांश अन्य सामान्य वस्तुओं के रूप में, जो आपको अपने आवेदन को ट्यून करते समय केवल ढेर के आकार का प्रबंधन करने की अनुमति देता है। तकनीकी रूप से, यह अकेले आपके जावा 7 कार्यक्रमों में String.intern () का उपयोग करके पुनर्विचार करने का पर्याप्त कारण हो सकता है। लेकिन अन्य कारण भी हैं।

स्ट्रिंग पूल मूल्य कचरा एकत्र किए जाते हैं

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

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

स्रोत।


11

जैसा कि अन्य उत्तर बताते हैं कि जावा में मेमोरी दो भागों में विभाजित है

1. स्टैक: एक स्टैक प्रति थ्रेड बनाया जाता है और यह स्टैक फ्रेम को स्टोर करता है जो फिर से लोकल वेरिएबल्स को स्टोर करता है और यदि वेरिएबल एक रेफरेंस टाइप है तो वह वेरिएबल वास्तविक ऑब्जेक्ट के लिए हीप में मेमोरी लोकेशन को संदर्भित करता है।

2. ढेर: सभी प्रकार की वस्तुओं को हीप में बनाया जाएगा।

हीप मेमोरी को फिर से 3 भागों में विभाजित किया गया है

1. यंग जनरेशन: जिन वस्तुओं का अल्प जीवन होता है, यंग जनरेशन को दो श्रेणियों ईडन स्पेस और सर्वाइवर स्पेस में विभाजित किया जा सकता है ।

2. पुरानी पीढ़ी: स्टोर ऑब्जेक्ट्स जो कई कचरा संग्रह चक्रों से बच गए हैं और अभी भी संदर्भित किए जा रहे हैं।

3. परमानेंट जेनरेशन: प्रोग्राम के बारे में मेटाडेटा स्टोर जैसे रनटाइम स्थिर पूल।

स्ट्रिंग पूल निरंतर हीप मेमोरी के स्थायी पीढ़ी क्षेत्र के अंतर्गत आता है।

हम bytecode में हमारे कोड के लिए रनटाइम निरंतर पूल देख सकते हैं, javap -verbose class_nameजिसके उपयोग से हमें विधि संदर्भ (#Methodref), क्लास ऑब्जेक्ट (#Class), स्ट्रिंग शाब्दिक (#String) दिखाई देंगे

क्रम-निरंतर पूल

आप इसके बारे में मेरे लेख पर पढ़ सकते हैं कि कैसे JVM हैंडल मेथड ओवरलोडिंग और आंतरिक रूप से ओवरराइडिंग करता है


कृपया किसी भी संबद्धता का खुलासा करें और पोस्टिंग के माध्यम से अपनी साइट को बढ़ावा देने के लिए साइट के रूप में उपयोग न करें। देखिए मैं एक अच्छा उत्तर कैसे लिखूं?

9

उन महान उत्तरों के लिए जो पहले से ही यहां शामिल हैं, मैं कुछ ऐसा जोड़ना चाहता हूं जो मेरे परिप्रेक्ष्य में गायब है - एक चित्रण।

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

इसलिए, उदाहरण के लिए, यदि आप निम्नलिखित वस्तुओं को प्राप्त करते हैं:

String s1 = "abc"; 
String s2 = "123";
String obj1 = new String("abc");
String obj2 = new String("def");
String obj3 = new String("456);

स्ट्रिंग शाब्दिक s1और s2स्ट्रिंग निरंतर पूल में जाएंगे, ऑब्जेक्ट obj1, obj2, obj3 ढेर तक। उन सभी को, स्टैक से संदर्भित किया जाएगा।

इसके अलावा, कृपया ध्यान दें कि "एबीसी" ढेर में और स्ट्रिंग निरंतर पूल में दिखाई देगा। क्यों String s1 = "abc"और String obj1 = new String("abc")इस तरह से बनाया जाएगा? यह इसलिए है क्योंकि String obj1 = new String("abc")स्पष्ट रूप से एक स्ट्रिंग ऑब्जेक्ट का एक नया और संदर्भित अलग-अलग उदाहरण बनाता है और String s1 = "abc"स्ट्रिंग स्थिर पूल से एक उदाहरण का पुन: उपयोग कर सकता है यदि कोई उपलब्ध है। अधिक विस्तृत विवरण के लिए: https://stackoverflow.com/a/3298542/2811258

यहां छवि विवरण दर्ज करें


दिए गए आरेख में, "शाब्दिक" और "456" शब्द कहां मौजूद होंगे। और ये कैसे संदर्भित होंगे?
सत्येंद्र

आपकी टिप्पणी के लिए धन्यवाद @ सत्येंद्र, मैंने दृष्टांत और उत्तर को अपडेट कर दिया है।
जॉनी

@Stas क्यों एक और स्ट्रिंग ऑब्जेक्ट "एबीसी" बनाया गया है..इसको शाब्दिक अधिकार को इंगित करने के लिए संदर्भ obj1 का उपयोग करना चाहिए?

यह इसलिए है क्योंकि स्ट्रिंग obj1 = नया स्ट्रिंग ("एबीसी") स्पष्ट रूप से एक स्ट्रिंग ऑब्जेक्ट का एक नया और संदर्भित विशिष्ट उदाहरण बनाता है और स्ट्रिंग ए 1 = "एबीसी" स्ट्रिंग निरंतर पूल से एक उदाहरण का पुन: उपयोग कर सकता है यदि कोई उपलब्ध है। अधिक विस्तृत विवरण के लिए: stackoverflow.com/a/3298542/2811258
जॉनी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.