स्मृति को सहेजने के लिए स्ट्रिंग में "" क्यों जोड़ा जाता है?


193

मैंने इसमें बहुत सारे डेटा के साथ एक वैरिएबल का उपयोग किया, कहते हैं String data। मैं निम्नलिखित तरीके से इस स्ट्रिंग के एक छोटे हिस्से का उपयोग करना चाहता था:

this.smallpart = data.substring(12,18);

डिबगिंग के कुछ घंटों के बाद (एक मेमोरी विज़ुअलाइज़र के साथ) मुझे पता चला कि ऑब्जेक्ट फ़ील्ड smallpartने सभी डेटा को याद किया data, हालांकि इसमें केवल सबस्ट्रिंग शामिल था।

जब मैंने कोड को इसमें बदला:

this.smallpart = data.substring(12,18)+""; 

..समस्या का निदान हो गया था! अब मेरा आवेदन अब बहुत कम मेमोरी का उपयोग करता है!

वो कैसे संभव है? क्या कोई इसे समझा सकता है? मुझे लगता है कि यह। डेटा, डेटा की ओर संदर्भित करता रहा, लेकिन क्यों?

अद्यतन: मैं फिर बड़ी स्ट्रिंग को कैसे साफ़ कर सकता हूँ? क्या डेटा = नया स्ट्रिंग (data.substring (0,100)) बात करेगा?


नीचे अपने अंतिम इरादे के बारे में अधिक पढ़ना: पहली जगह में बड़ी स्ट्रिंग कहां से आती है? यदि किसी फ़ाइल या डेटाबेस CLOB या कुछ और से पढ़ा जाता है, तो केवल पढ़ने के दौरान आपको जो कुछ भी चाहिए, उसे पढ़ना सभी तरह से इष्टतम होगा।
पीएसपीड

4
कमाल है ... मैं जावा में 4 से 5 साल से अधिक काम कर रहा हूं, फिर भी यह मेरे लिए नया है :)। जानकारी भाई के लिए धन्यवाद।
पार्थ

1
उपयोग करने के लिए एक सूक्ष्मता है new String(String); देख stackoverflow.com/a/390854/8946
लॉरेंस डॉल

जवाबों:


159

निम्न कार्य करना:

data.substring(x, y) + ""

एक नई (छोटी) स्ट्रिंग ऑब्जेक्ट बनाता है, और विकल्प () द्वारा बनाई गई स्ट्रिंग के संदर्भ को दूर फेंक देता है, इस प्रकार यह कचरा संग्रह को सक्षम करता है।

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

अधिक जानकारी के लिए JDK स्ट्रिंग स्रोत में प्रतिस्थापन () विधि पर एक नज़र डालें ।

संपादित करें: आपके पूरक प्रश्न का उत्तर देने के लिए, सबस्ट्रिंग से एक नई स्ट्रिंग का निर्माण आपकी मेमोरी की खपत को कम करेगा, बशर्ते आप मूल स्ट्रिंग के किसी भी संदर्भ को बिन करें।

नोट (जनवरी 2013)। उपरोक्त व्यवहार जावा 7u6 में बदल गया है । फ्लाईवेट पैटर्न का अब उपयोग नहीं किया जाता है और यह आपकी इच्छानुसार substring()काम करेगा।


89
यह बहुत कम मामलों में से एक है, जहां String(String)कंस्ट्रक्टर (यानी स्ट्रिंग कंस्ट्रक्टर को एक स्ट्रिंग को इनपुट के रूप में लिया जाता है) उपयोगी है: new String(data.substring(x, y))प्रभावी रूप से अपील करने के समान ही काम करता है "", लेकिन यह आशय को कुछ हद तक स्पष्ट करता है।
जोआचिम सॉउर

3
बस सटीक करने के लिए, valueमूल स्ट्रिंग की विशेषता का उपयोग करता है । मुझे लगता है कि इसीलिए संदर्भ रखा गया है।
वैलेंटाइन रोचर

@ बिशिबोश - हाँ, यह सही है। मैं कार्यान्वयन की विशिष्टताओं को उजागर नहीं करना चाहता था, लेकिन ठीक यही हो रहा है।
ब्रायन एग्न्यू

5
तकनीकी रूप से यह एक कार्यान्वयन विवरण है। लेकिन यह फिर भी निराशाजनक है, और बहुत सारे लोगों को पकड़ता है।
ब्रायन एग्न्यू

1
मुझे आश्चर्य है कि अगर कमजोर संदर्भ या ऐसे का उपयोग करके जेडीके में इसे अनुकूलित करना संभव है। अगर मैं अंतिम व्यक्ति हूं जिसे इस चार [] की आवश्यकता है, और मुझे केवल इसकी थोड़ी आवश्यकता है, तो आंतरिक रूप से उपयोग करने के लिए मेरे लिए एक नया सरणी बनाएं।
डब्ल्यूडब्ल्यू।

28

यदि आप के स्रोत को substring(int, int)देखते हैं, तो आप देखेंगे कि यह रिटर्न करता है:

new String(offset + beginIndex, endIndex - beginIndex, value);

जहां valueमूल है char[]। तो आपको एक नया स्ट्रिंग मिलता है लेकिन उसी अंतर्निहित के साथ char[]

जब आप ऐसा करते हैं, data.substring() + ""तो आपको एक नई अंतर्निहित के साथ एक नया स्ट्रिंग मिलता है char[]

वास्तव में, आपका उपयोग मामला एकमात्र ऐसी स्थिति है जहां आपको String(String)कंस्ट्रक्टर का उपयोग करना चाहिए :

String tiny = new String(huge.substring(12,18));

1
उपयोग करने के लिए एक सूक्ष्मता है new String(String); देख stackoverflow.com/a/390854/8946
लॉरेंस डोल

17

जब आप उपयोग करते हैं substring, तो यह वास्तव में एक नया स्ट्रिंग नहीं बनाता है। यह अभी भी एक ऑफसेट और आकार की कमी के साथ आपके मूल स्ट्रिंग को संदर्भित करता है।

इसलिए, अपने मूल स्ट्रिंग को एकत्र करने की अनुमति देने के लिए, आपको एक नया स्ट्रिंग बनाने (उपयोग करने new String, या जो आपको मिला है) बनाने की आवश्यकता है।


5

मुझे लगता है कि यह। डेटा, डेटा की ओर संदर्भित करता रहा, लेकिन क्यों?

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

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

इसे ठीक करने का "सही" तरीका new String(String)कंस्ट्रक्टर है, अर्थात

this.smallpart = new String(data.substring(12,18));

BTW, कुल मिलाकर सबसे अच्छा समाधान यह होगा कि पहली बार में बहुत बड़े स्ट्रिंग्स होने से बचें, और किसी भी इनपुट को छोटे समय में, कुछ केबी को एक बार में संसाधित करें।


उपयोग करने के लिए एक सूक्ष्मता है new String(String); देख stackoverflow.com/a/390854/8946
लॉरेंस डोल

5

जावा स्ट्रिंग्स में इम्पोर्टेबल ऑब्जेक्ट्स होते हैं और एक बार स्ट्रिंग बनाने के बाद, यह तब तक मेमोरी पर रहता है जब तक कि यह कचरा कॉलेक्टर द्वारा साफ नहीं किया जाता है (और यह सफाई ऐसी कोई चीज़ नहीं है जिसे आप ले सकते हैं)।

जब आप सबस्ट्रिंग विधि कहते हैं, तो जावा ट्रूली नया स्ट्रिंग नहीं बनाता है, लेकिन मूल स्ट्रिंग के अंदर सिर्फ वर्णों की एक श्रृंखला संग्रहीत करता है।

इसलिए, जब आपने इस कोड के साथ एक नया स्ट्रिंग बनाया:

this.smallpart = data.substring(12, 18) + ""; 

जब आप खाली स्ट्रिंग के साथ परिणाम को बदलते हैं तो आपने वास्तव में एक नया स्ट्रिंग बनाया था। इसीलिए।


3

1997 में jwz द्वारा प्रलेखित :

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


2

बस योग करने के लिए, यदि आप छोटी संख्या में बड़े तार से बहुत सारे सबस्ट्रिंग बनाते हैं, तो उपयोग करें

   String subtring = string.substring(5,23)

चूँकि आप केवल बड़े तारों को संग्रहीत करने के लिए स्थान का उपयोग करते हैं, लेकिन यदि आप बड़े तारों के खो जाने से, छोटे तारों का एक छोटा सा हिस्सा निकाल रहे हैं, तो

   String substring = new String(string.substring(5,23));

अपनी स्मृति का उपयोग करते रहेंगे, क्योंकि बड़े तार को फिर से प्राप्त किया जा सकता है जब जरूरत नहीं होगी।

जिसे आप कहते हैं new Stringवह एक सहायक अनुस्मारक है जिसे आप वास्तव में मूल स्ट्रिंग के संदर्भ के बजाय एक नया स्ट्रिंग प्राप्त कर रहे हैं।


उपयोग करने के लिए एक सूक्ष्मता है new String(String); देख stackoverflow.com/a/390854/8946
लॉरेंस डोल

2

सबसे पहले, कॉलिंग java.lang.String.substringStringमूल सरणी के महत्वपूर्ण भाग को कॉपी करने के बजाय ऑफसेट और लंबाई के उपयोग के साथ मूल पर नई विंडो बनाता है

अगर हम substringविधि पर एक करीब से नज़र डालें तो हम एक स्ट्रिंग कंस्ट्रक्टर कॉल को नोटिस करेंगे String(int, int, char[])और इसे पूरे पास करेंगे char[]जो स्ट्रिंग का प्रतिनिधित्व करता है । इसका मतलब है कि विकल्प स्ट्रिंग में मूल स्ट्रिंग जितनी अधिक मेमोरी होगी ।

ठीक है, लेकिन + ""इसके बिना कम स्मृति की मांग में परिणाम क्यों ??

एक कर +पर stringsके माध्यम से कार्यान्वित किया जाता है StringBuilder.appendविधि कॉल। AbstractStringBuilderकक्षा में इस पद्धति के कार्यान्वयन के बारे में बताएं जो हमें बताएगी कि यह अंत arraycopyमें उस हिस्से के साथ है जिसकी हमें वास्तव में आवश्यकता है ( substring)।

कोई और काम?

this.smallpart = new String(data.substring(12,18));
this.smallpart = data.substring(12,18).intern();

0

स्ट्रिंग में "" लागू करने से कभी-कभी मेमोरी बच जाएगी ।

मान लीजिए कि मेरे पास एक विशाल स्ट्रिंग है जिसमें पूरी पुस्तक, एक मिलियन अक्षर हैं।

फिर मैं पुस्तक के अध्यायों को मिलाकर 20 तार बनाता हूं।

फिर मैं सभी पैराग्राफ वाले 1000 तार बनाता हूं।

फिर मैं सभी वाक्यों से युक्त 10,000 तार बनाता हूँ।

फिर मैं सभी शब्दों से युक्त 100,000 तार बनाता हूं।

मैं अभी भी केवल 1,000,000 वर्णों का उपयोग करता हूं। यदि आप प्रत्येक अध्याय, पैराग्राफ, वाक्य और शब्द में "" जोड़ते हैं, तो आप 5,000,000 वर्णों का उपयोग करते हैं।

बेशक यह पूरी तरह से अलग है अगर आप केवल एक ही शब्द को पूरी किताब से निकालते हैं, और पूरी किताब को इकट्ठा किया जा सकता है लेकिन ऐसा नहीं है क्योंकि यह एक शब्द इसके लिए एक संदर्भ रखता है।

और यह फिर से अलग है यदि आपके पास एक मिलियन कैरेक्टर स्ट्रिंग है और दोनों सिरों पर टैब और रिक्त स्थान हटा दें, तो एक प्रतिस्थापन बनाने के लिए 10 कॉल करें। जिस तरह से जावा काम करता है या काम करता है वह हर बार एक लाख अक्षरों की नकल करने से बचता है। समझौता है, और यह अच्छा है अगर आप जानते हैं कि समझौता क्या हैं।

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