क्या java.lang.String.intern () का उपयोग करना अच्छा है?


194

Javadoc के बारे में String.intern()बहुत विस्तार नहीं देता है। (संक्षेप में: यह स्ट्रिंग के विहित निरूपण को वापस लौटाता है, जिससे उपयोग किए गए तार को तुलना की जा सकती है ==)

  • मैं इस समारोह का उपयोग कब करूंगा String.equals()?
  • क्या जेवाडोक में साइड इफेक्ट्स का उल्लेख नहीं किया गया है, यानी जेआईटी कंपाइलर द्वारा अधिक या कम अनुकूलन?
  • क्या इसके और उपयोग हैं String.intern()?

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

हां, इंटर्न () का अपना प्रदर्शन प्रभाव है। विशेष रूप से क्योंकि इंटर्न () लागत रैखिक रूप से बढ़ जाती है क्योंकि आप आंतरिक तार करते हैं और उनके लिए एक संदर्भ रखते हैं। कम से कम एक सूर्य / परिक्रमा 1.6.0_30 वी.एम.
लैक्रिक्स 1547

जवाबों:


125

मैं String.equals () के पक्ष में इस फ़ंक्शन का उपयोग कब करूंगा

जब आपको गति की आवश्यकता होती है, तो आप संदर्भ द्वारा तार की तुलना कर सकते हैं (== बराबर से तेज़ है)

क्या जवादोक में साइड इफेक्ट्स का उल्लेख नहीं किया गया है?

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

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

(JGuru से)

तीसरा नुकसान (केवल जावा 7 या उससे कम): इंटर्नल स्ट्रिंग्स पर्मगेन स्पेस में रहते हैं, जो आमतौर पर काफी छोटा होता है; आप एक मुक्त ढेर स्थान के साथ एक OutOememoryError में चला सकते हैं।

(माइकल बोर्गवर्ड से)


64
एक तीसरा नुकसान: इंटर्नल स्ट्रिंग्स पर्मगेन स्पेस में रहते हैं, जो आमतौर पर काफी छोटा होता है; आप एक मुक्त ढेर स्थान के साथ एक OutOememoryError में चला सकते हैं।
माइकल बोर्गवर्ड

15
AFAIK नए VMs भी PermGen स्पेस इकट्ठा करते हैं।
डैनियल रिकोव्स्की

31
इंटर्न मेमोरी मैनेजमेंट के बारे में है, तुलना गति से नहीं। के बीच का अंतर if (s1.equals(s2))और if (i1 == i2)कम से कम जब तक आपके ही प्रमुख पात्रों के साथ लंबे समय से तार का एक बहुत है। अधिकांश वास्तविक दुनिया में उपयोग (URL के अलावा) तार पहले कुछ वर्णों के भीतर अलग-अलग होंगे। और लंबी अगर-और चेन वैसे भी एक कोड गंध हैं: एनम और फ़नकार के नक्शे का उपयोग करें।
kdgregory

25
आप अभी भी अपने पूरे कार्यक्रम में s1.equals सिंटैक्स का उपयोग कर सकते हैं, न ही उपयोग ==, .equals का उपयोग == आंतरिक रूप से शॉर्ट-सर्किट मूल्यांकन के लिए
gtrak

15
माइकल बोर्गवर्ड ने यह नहीं कहा कि नजरबंद स्ट्रिंग्स को इकट्ठा नहीं किया जा सकता है। और वह एक FALSE जोर है। माइकल की टिप्पणी (सही ढंग से) इससे अधिक सूक्ष्म है।
स्टीफन सी

193

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


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

16
स्पष्ट करने की आवश्यकता है - इंटाइलिंग हमेशा संकलन-समय स्थिर स्ट्रिंग्स (शाब्दिक और निश्चित अभिव्यक्तियों) के लिए स्वचालित रूप से होता है। इसके अतिरिक्त यह तब होता है जब String.intern () को रनटाइम पर डायनामिकली स्ट्रिंग्स का मूल्यांकन किया जाता है।
सर्वश्रेष्ठ

तो आपका मतलब है, अगर हीप में "हैलो" की 1000 वस्तुएं हैं और मैं उनमें से एक पर इंटर्न () प्रदर्शन करता हूं, तो बाकी 999 ऑब्जेक्ट्स स्वचालित रूप से नष्ट हो जाएंगे?
अरुण राज

@ अरुणराज, आप अपने 1000 को अभी भी ढेर पर रखेंगे, और इंटर्न पूल में एक अतिरिक्त , जो बाद में str.intern()जब भी हो, फिर से उपयोग के लिए तैयार हो सकता strहै "Hello"
Matthieu

37

String.intern()निश्चित रूप से आधुनिक जेवीएम में कचरा एकत्र किया जाता है।
जीसी गतिविधि के कारण निम्न मेमोरी से बाहर चलता है:

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

गैर GCed String.intern () के मिथक पर और अधिक (मेरे से) देखें ।


26
OutOfMemoryException- नहीं, मेरे मस्तिष्क में ऊपर का कोड नहीं : javaturning लेख का लिंक, जो इस लेख की ओर इशारा कर रहा है, जो कि javaturning लेख की ओर इशारा कर रहा है, जो ... :-)
user85421

यद्यपि आप देख सकते हैं कि उस लिंक को जोड़ने के लिए पोस्ट को संपादित किया गया था;)
12:56 बजे रेकिंग

3
आप यह उल्लेख करना चाहते हैं कि आप जिस बाहरी संदर्भ से लिंक करते हैं, उसके भी आप लेखक हैं।
Thorbjørn रावन एंडरसन

11
@Carlos एक बाहरी संदर्भ को जोड़ता है जो स्टैकओवरफ़्लो से लिंक करता है .. एक कारण होना चाहिए ... Stackoverflow :)
Seiti

2
@Seiti परिपत्र संदर्भ आसानी से इन दिनों पता चला है: पी
अजय

16

मैंने हाल ही में Java 6, 7 और 8: String.intern (जावा 6, 7 और 8 - स्ट्रिंग पूलिंग में String.intern) के कार्यान्वयन के बारे में एक लेख लिखा है ।

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

संक्षेप में:

  • String.intern()जावा 6 में जाने से बचें , क्योंकि यह पर्मगेन में जाता है
  • String.intern()जावा 7 और जावा 8 को प्राथमिकता दें: यह अपने स्वयं के ऑब्जेक्ट पूल को रोल करने की तुलना में 4-5x कम मेमोरी का उपयोग करता है
  • धुन के लिए सुनिश्चित करें -XX:StringTableSize(डिफ़ॉल्ट शायद बहुत छोटा है; एक अभाज्य संख्या निर्धारित करें)

3
कृपया अपने ब्लॉग में लिंक पोस्ट न करें, इसे कुछ लोग स्पैम के रूप में मानते हैं। प्लस ब्लॉग लिंक में 404 की मौत की उल्लेखनीय प्रवृत्ति है। कृपया या तो अपने लेख को इनलाइन संक्षेप में प्रस्तुत करें, या उस लिंक को प्रश्न के लिए टिप्पणी में छोड़ दें।
Mat

3
लिखने के लिए धन्यवाद कि @ mik1! बहुत जानकारीपूर्ण, स्पष्ट और अद्यतित लेख। (मैं खुद इसका लिंक पोस्ट करने के इरादे से यहां आया था।)
ल्यूक उशरवुड

1
-XX arg का उल्लेख करने के लिए धन्यवाद। आप इसका उपयोग तालिका के आँकड़े देखने के लिए भी कर सकते हैं: -XX: + PrintStringTableStatistics
csadler

13

तार की तुलना = = की तुलना में बहुत तेज़ है ()

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

String.intern () स्ट्रिंग को हीप से दूर खींचकर पर्मगेन में डालें

स्ट्रिंग किए गए आंतरिक को एक अलग भंडारण क्षेत्र में रखा जाता है: स्थायी पीढ़ी जो JVM का एक क्षेत्र है जो गैर-उपयोगकर्ता ऑब्जेक्ट्स के लिए आरक्षित है, जैसे क्लास, मेथड और अन्य आंतरिक JVM ऑब्जेक्ट। इस क्षेत्र का आकार सीमित है और ढेर की तुलना में बहुत कीमती है। इस क्षेत्र को हीप से छोटा होने के कारण सभी स्थान का उपयोग करने और आउटऑफ़मेरी एक्ससेप्शन प्राप्त करने की अधिक संभावना है।

String.intern () स्ट्रिंग कचरा एकत्र किया जाता है

जेवीएम के नए संस्करणों में भी किसी भी वस्तु द्वारा संदर्भित नहीं किए जाने पर आंतरिक रूप से स्ट्रिंग को इकट्ठा किया जाता है।

उपरोक्त 3 बिंदु को ध्यान में रखते हुए आप यह घटा सकते हैं कि स्ट्रिंग इंटर्न () केवल कुछ ही स्थिति में उपयोगी हो सकती है जब आप बहुत अधिक स्ट्रिंग तुलना करते हैं, हालांकि यह बेहतर है कि आंतरिक स्ट्रिंग का उपयोग न करें यदि आप वास्तव में नहीं जानते कि आप क्या करते हैं कर रहे हैं ...



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

7

मैं String.equals () के पक्ष में इस फ़ंक्शन का उपयोग कब करूंगा

यह देखते हुए कि वे अलग-अलग काम करते हैं, शायद कभी नहीं।

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

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

यह लगभग हमेशा एक UserIdप्रकार बनाने के लिए बेहतर होता है जिसे इंटर्न किया जाता है (यह थ्रेड-सेफ जेनेरिक इंटर्निग मैकेनिज्म बनाना आसान है) और ओपन एनुम की तरह कार्य करता है, java.lang.Stringयदि यूजर आईडी होने के कारण टाइप को संदर्भ शब्दार्थ के साथ अधिभारित किया जाता है।

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


6

किसी भी फायदे के बारे में पता नहीं है, और अगर कोई एक में होता है कि बराबर () खुद का उपयोग करेगा () आंतरिक रूप से (जो यह नहीं है)।

बस्टिंग इंटर्न () मिथक


7
आपके यह कहने के बावजूद कि आप किसी भी लाभ से अवगत नहीं हैं, आपकी पोस्ट की गई लिंक == के माध्यम से तुलना की पहचान करती है = जैसा कि 5x तेज है और इस प्रकार पाठ-केंद्रित प्रदर्शन कोड के लिए महत्वपूर्ण है
ब्रायन एग्न्यू

3
जब आपके पास बहुत सारे पाठ-तुलना करने के लिए होता है तो आप अंततः PermGen स्पेस से बाहर निकल जाएंगे। जब गति में अंतर करने के लिए इतना पाठ-तुलना नहीं होती है तो कोई बात नहीं। किसी भी तरह, बस अपने तार नहीं ()। यह इसके लायक नहीं है।
बॉम्बे

यह भी कहा जाता है कि समग्र सापेक्ष लाभ आमतौर पर छोटा होने वाला है।
वस्तुओं

मुझे नहीं लगता कि इस तरह का तर्क मान्य है। अच्छा लिंक हालांकि!
डैनियल रिकोव्स्की जूल

1
@DR: क्या तर्क है? यह एक बड़ी गिरावट है। @ टिप्पणी: क्षमा करें, लेकिन आपके तर्क कारणों से कम हैं। हैं बहुत उपयोग करने के लिए अच्छे कारण intern, और बहुत अच्छा कारण है कि equalsडिफ़ॉल्ट रूप से ऐसा करने नहीं देता है। आपके द्वारा पोस्ट किया गया लिंक पूरा बोललॉक है। अंतिम पैराग्राफ भी मानता है कि internएक वैध उपयोग परिदृश्य है: भारी पाठ प्रसंस्करण (जैसे एक पार्सर)। यह कहते हुए कि "[XYZ] खतरनाक है यदि आप नहीं जानते कि आप क्या कर रहे हैं" तो यह बहुत ही प्रतिबंधात्मक है कि यह शारीरिक रूप से दर्द देता है।
कोनराड रूडोल्फ

4

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

इसके अलावा जावा 7 में, इनरंग स्ट्रिंग्स कोई लंबे समय तक पर्मगेन में नहीं रहते बल्कि इसके बजाय ढेर होते हैं। तो आपको इसके आकार के बारे में चिंता करने की ज़रूरत नहीं है और हाँ यह कचरा इकट्ठा हो जाता है:

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


मुझे दूसरा होना चाहिए: मेरे सॉफ़्टवेयर पर, एक ढेर डंप ने दिखाया कि अधिकांश हीप स्थान का उपयोग Stringउदाहरणों द्वारा किया गया था । जब उनकी सामग्री को देखते हुए, मैंने कई डुप्लिकेट देखे और स्विच करने का फैसला किया intern(), जिससे सैकड़ों एमबी बच गए।
मैथ्यू

4

क्या जेवाडोक में साइड इफेक्ट्स का उल्लेख नहीं किया गया है, यानी जेआईटी कंपाइलर द्वारा अधिक या कम अनुकूलन?

मुझे जेआईटी स्तर के बारे में नहीं पता है, लेकिन स्ट्रिंग पूल के लिए प्रत्यक्ष बायोटेक समर्थन है , जो कि एक समर्पित CONSTANT_String_infoसंरचना के साथ जादुई रूप से और कुशलता से लागू किया जाता है (अधिकांश अन्य वस्तुओं के विपरीत जो अधिक सामान्य प्रतिनिधित्व करते हैं)।

JVMs

JVMS 7 5.1 कहता है :

एक स्ट्रिंग शाब्दिक वर्ग स्ट्रिंग के एक उदाहरण के लिए एक संदर्भ है, और एक वर्ग या इंटरफ़ेस के द्विआधारी प्रतिनिधित्व में एक CONSTANT_String_info संरचना (fo4.4.3) से लिया गया है। CONSTANT_String_info संरचना यूनिकोड कोड बिंदुओं के अनुक्रम को स्ट्रिंग शाब्दिक रूप देता है।

जावा प्रोग्रामिंग लैंग्वेज के लिए आवश्यक है कि समान स्ट्रिंग शाब्दिक (यानी, शाब्दिक जिसमें कोड अंकों का समान अनुक्रम हो) को कक्षा स्ट्रिंग (JLS §3.10.5) के समान उदाहरण को संदर्भित करना होगा। इसके अलावा, यदि String.intern को किसी भी स्ट्रिंग पर विधि कहा जाता है, तो परिणाम उसी वर्ग उदाहरण के लिए एक संदर्भ है जो कि यदि स्ट्रिंग एक शाब्दिक के रूप में दिखाई देता है तो उसे लौटा दिया जाएगा। इस प्रकार, निम्नलिखित अभिव्यक्ति का मूल्य सही होना चाहिए:

("a" + "b" + "c").intern() == "abc"

एक स्ट्रिंग शाब्दिक प्राप्त करने के लिए, जावा वर्चुअल मशीन CONSTANT_String_info संरचना द्वारा दिए गए कोड बिंदुओं के अनुक्रम की जांच करता है।

  • यदि विधि String.intern को पहले कक्षा स्ट्रिंग के एक उदाहरण पर बुलाया गया है, जिसमें CONSTANT_String_info संरचना द्वारा दिए गए यूनिकोड कोड बिंदुओं का एक अनुक्रम है, तो स्ट्रिंग शाब्दिक व्युत्पत्ति का परिणाम कक्षा स्ट्रिंग के उसी उदाहरण का संदर्भ है।

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

बाईटकोड

यह भी OpenJDK 7 पर बायोटेक कार्यान्वयन को देखने के लिए शिक्षाप्रद है।

अगर हम सड़ जाते हैं:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

हमारे पास निरंतर पूल है:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

और main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

नोट कैसे:

  • 0और 3: एक ही ldc #2निरंतर लोड किया जाता है (शाब्दिक)
  • 12: एक नया स्ट्रिंग उदाहरण बनाया गया है ( #2तर्क के रूप में)
  • 35: aऔर cनियमित वस्तुओं के साथ तुलना की जाती हैif_acmpne

निरंतर तार का प्रतिनिधित्व बाइटकोड पर काफी जादू है:

और ऊपर दिए गए JVMS उद्धरण से लगता है कि जब भी यूटीएफ 8 को इंगित किया जाता है, तब समान उदाहरणों को लोड किया जाता है ldc

मैंने खेतों के लिए इसी तरह के परीक्षण किए हैं, और:

बोनस : इसकी तुलना इंटेगर पूल से करें , जिसमें डायरेक्ट बायटेकोड सपोर्ट नहीं है (यानी कोई CONSTANT_String_infoएनालॉग नहीं )।


2

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


2

subString()सोर्स स्ट्रिंग की तुलना में रिजल्ट छोटा होने पर एक तरह की मेमोरी लीकेज आ सकती है और ऑब्जेक्ट की लाइफ लंबी होती है।

सामान्य समाधान का उपयोग करना है new String( s.subString(...))लेकिन जब आपके पास एक वर्ग होता है जो संभावित / संभावना के परिणाम को संग्रहीत करता है subString(...)और कॉलर पर कोई नियंत्रण नहीं होता है, तो आप intern()कंस्ट्रक्टर को दिए गए स्ट्रिंग तर्कों को संग्रहीत करने पर विचार कर सकते हैं । यह संभावित बड़े बफर को रिलीज़ करता है।


दिलचस्प है, लेकिन शायद यह कार्यान्वयन पर निर्भर है।
akostadinov

1
उपर्युक्त संभावित स्मृति रिसाव जावा 1.8 और 1.7.06 में नहीं होता है (और नए) जावा 1.7.0_06 में किए गए आंतरिक प्रतिनिधित्व के परिवर्तन को देखते हैं ।
उन्मूलन

माइक्रो-ऑप्टिमाइज़ेशन की पुष्टि करता है कि केवल प्रदर्शन और / या मेमोरी प्रोफाइलिंग के बाद आवश्यक होने पर ही इसे लागू किया जाना चाहिए। धन्यवाद।
अकोस्टैडिनोव

2

स्ट्रिंग इंटर्निंग उस स्थिति में उपयोगी है जहां equals()विधि को अक्सर लागू किया जाता है क्योंकि equals()विधि यह देखने के लिए त्वरित जांच करती है कि क्या वस्तु विधि की शुरुआत में समान हैं।

if (this == anObject) {
    return true;
}

यह आमतौर पर तब होता है जब किसी Collectionअन्य कोड के माध्यम से खोज करने पर स्ट्रिंग समानता जांच भी हो सकती है।

इंटर्निंग से जुड़ी एक लागत है, हालांकि, मैंने कुछ कोड का माइक्रोबेनमार्क किया और पाया कि इंटर्निंग प्रक्रिया 10 के कारक द्वारा रनटाइम को बढ़ाती है।

इंटर्निंग करने के लिए सबसे अच्छी जगह आमतौर पर होती है जब आप कुंजी पढ़ रहे होते हैं जो कोड के बाहर संग्रहीत होते हैं क्योंकि कोड में तार स्वचालित रूप से इंटर्न होते हैं। यह आमतौर पर आपके एप्लिकेशन के शुरुआती चरणों में होता है ताकि प्रथम-उपयोगकर्ता पेनल्टी को रोका जा सके।

एक अन्य स्थान जहां यह किया जा सकता है, उपयोगकर्ता इनपुट को संसाधित करते समय जिसका उपयोग कुंजी लुकअप करने के लिए किया जा सकता है। यह सामान्य रूप से आपके अनुरोध प्रोसेसर में होता है, ध्यान दें कि इंटर्न स्ट्रिंग्स को नीचे पारित किया जाना चाहिए।

इसके अलावा कोड के बाकी हिस्सों में इंटर्न करने का कोई मतलब नहीं है क्योंकि यह आमतौर पर कोई लाभ नहीं देगा।


1

मैं इसे रखरखाव की परेशानी के लायक नहीं मानूंगा।

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


1

http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

के अनुसार वस्तुओं की तुलना करने के लिए String.equals()उपयोग करता है कि जोर देता है"=="String

http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html

यह स्ट्रिंग्स की लंबाई और फिर सामग्री की तुलना करता है।

(वैसे, एक बिक्री सूची में उत्पाद कोड स्ट्रिंग सभी समान लंबाई के लिए उत्तरदायी हैं - BIC0417 एक साइकिल चालक की सुरक्षा हेलमेट है, TIG0003 एक जीवित वयस्क नर बाघ है - आपको उनमें से एक को ऑर्डर करने के लिए सभी प्रकार के लाइसेंस की आवश्यकता है। शायद आप एक ही समय में एक सुरक्षा हेलमेट को बेहतर ढंग से ऑर्डर कर सकते हैं।]

तो ऐसा लगता है जैसे आपको अपने स्ट्रिंग्स को उनके intern()संस्करण के साथ बदलने से लाभ मिलता है , लेकिन आपको equals()अपनी प्रोग्रामिंग में "==" का उपयोग करके सुरक्षा - और पठनीयता और मानक अनुपालन - -साथ का उपयोग करने की सुविधा मिलती है । और जो मैं कहने जा रहा हूं, उसमें से अधिकांश इस बात पर निर्भर करता है कि यह सच है, अगर यह सच है।

लेकिन String.equals()परीक्षण करता है कि आपने इसे उपयोग करने से पहले एक स्ट्रिंग और कुछ अन्य ऑब्जेक्ट नहीं पारित किया है "=="? मैं कहने के लिए योग्य नहीं हूं, लेकिन मैं अनुमान नहीं लगाऊंगा, क्योंकि अत्यधिक ऐसे अधिकांश equals()ऑपरेशन स्ट्रिंग टू स्ट्रिंग होंगे, ताकि परीक्षण लगभग हमेशा पास हो। वास्तव में, "==" को प्राथमिकता देने से String.equals()एक विश्वास पैदा होता है कि आप अक्सर स्ट्रिंग की तुलना उसी वास्तविक वस्तु से कर रहे हैं।

मुझे आशा है कि कोई भी आश्चर्यचकित नहीं होगा कि निम्नलिखित लाइनें "गलत" का परिणाम हैं:

    Integer i = 1;
    System.out.println("1".equals(i));

लेकिन अगर आप को बदलने iके लिए i.toString()दूसरी पंक्ति में, निश्चित रूप से यह है true

वे स्थान जहाँ आप इंटर्निंग से लाभ की उम्मीद कर सकते हैं , Setऔर Mapजाहिर है। मुझे आशा है कि नजरबंद तार उनके हैशकोड को कैश किया गया है ... मुझे लगता है कि यह एक आवश्यकता होगी। और मुझे आशा है कि मैंने अभी एक विचार नहीं दिया है जो मुझे एक मिलियन डॉलर कमा सकता है। :-)

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

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

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


0

इंटर्न का उपयोग करने का वास्तविक कारण उपरोक्त नहीं है। मेमोरी आउट होने के बाद आपको इसका उपयोग करना पड़ता है। एक विशिष्ट कार्यक्रम में स्ट्रिंग के बहुत सारे अन्य स्ट्रिंग के स्ट्रिंगर.सुबस्ट्रिंग () हैं [100K xml फ़ाइल से उपयोगकर्ता-नाम निकालने के बारे में सोचें। जावा कार्यान्वयन यह है कि, सबस्ट्रिंग मूल स्ट्रिंग का संदर्भ रखता है और उस विशाल स्ट्रिंग में प्रारंभ + अंत होता है। (इसके पीछे का विचार उसी बड़े स्ट्रिंग का पुन: उपयोग है)

1000 बड़ी फ़ाइलों के बाद, जिनसे आप केवल 1000 संक्षिप्त नाम बचाते हैं, आप पूरे 1000 फाइलों को याद रखेंगे! समाधान: इस परिदृश्य में बस smallsubstring.intern () का उपयोग करें


यदि आपको इसकी आवश्यकता है तो सबस्ट्रिंग से नया स्ट्रिंग क्यों नहीं बनाया जाए?
Thorbjørn रेवन एंडरसन

0

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

तो क्या मेमोरी की खपत को कम करने के लिए इंटर्न () का विकल्प है, (== बनाम प्रदर्शन के लाभ के बराबर नहीं है)


0

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

यहाँ एक छोटी सी चाल है जो आपको कुछ नियंत्रण देती है कि आप किस प्रकार के तंत्र का उपयोग स्ट्रिंग्स और अन्य अपरिवर्तनों को आंतरिक करने के लिए करना चाहते हैं, और एक उदाहरण कार्यान्वयन:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

जब मैं स्ट्रीम से या रिजल्टसेट से फ़ील्ड पढ़ता हूं तो अक्सर उसका उपयोग करता हूं। नोट: LRUCacheएक साधारण कैश पर आधारित है LinkedHashMap<K,V>। यह स्वचालित रूप से retrieve()सभी कैश मिस के लिए उपयोगकर्ता द्वारा आपूर्ति की गई विधि कहता है ।

इसका उपयोग करने का तरीका LRUInternalizerआपके पढ़ने (या पढ़ने) से पहले एक बनाने के लिए है , स्ट्रिंग्स और अन्य छोटी अपरिवर्तनीय वस्तुओं को आंतरिक करने के लिए इसका उपयोग करें, फिर इसे मुक्त करें। उदाहरण के लिए:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}

0

मैं लगभग 36000 कोड की सामग्री को कैश करने के लिए इसका उपयोग कर रहा हूं जो संबद्ध नामों से लिंक करता है। मैं कैश में स्ट्रिंग्स को इंटर्न करता हूं क्योंकि कई कोड एक ही स्ट्रिंग को इंगित करते हैं।

अपने कैश में स्ट्रिंग्स को इंटर्न करके, मैं यह सुनिश्चित कर रहा हूं कि कोड उसी स्ट्रिंग को इंगित करें जो वास्तव में उसी मेमोरी को इंगित करता है, जिससे रैम की जगह बचती है।

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


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

0

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

मेरा सुझाव है कि जब आप अपने कोड के विशिष्ट भागों का अनुकूलन कर रहे हों तो स्ट्रिंग इंटर्निंग से ट्विन प्रदर्शन का उपयोग करें।

यह भी याद रखें कि स्ट्रिंग अपरिवर्तनीय है और इसके बारे में मूर्खतापूर्ण गलती न करें

String a = SOME_RANDOM_VALUE
a.intern()

करना याद है

String a = SOME_RANDOM_VALUE.intern()

0

यदि आप String.intern के लिए असीमित प्रतिस्थापन की तलाश कर रहे हैं, तो कचरा भी एकत्र किया गया है, निम्नलिखित मेरे लिए अच्छी तरह से काम कर रहा है।

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

बेशक, अगर आप मोटे तौर पर अनुमान लगा सकते हैं कि वहाँ कितने अलग-अलग तार होंगे, तो बस String.intern () with -XX: StringTableSize = highEnoughValue का उपयोग करें


SoftRef और अधिक सेंस बनाएगा।
vach

@vach को WeakReference (SoftReference के बजाय) का उपयोग करके मेमोरी को पहले ही मुक्त कर दिया जाता है ताकि अन्य आवंटन तेजी से आगे बढ़ सकें। यह इस बात पर निर्भर करता है कि आवेदन क्या कर रहा है, या तो कोई समझ सकता है।
bdruemen
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.