क्या लिंक्डलिस्ट तेज़ होगी? क्या मुझे `foreach` को `iterator` से बदलना चाहिए? क्या यह `ArrayList` एक `Array` होना चाहिए? यह लेख एक ऐसे अनुकूलन के जवाब में आया है जो इतना दुर्भावनापूर्ण है कि यह हमेशा के लिए मेरी याददाश्त में अंकित हो गया है।
जावा में सीधे जाने से पहले तथा हस्तक्षेप से निपटने के तरीकों पर विचार करने से पहले, चाहे वह कचरा संग्रहकर्ता से हो या संदर्भ स्विचिंग से, आइए पहले अपने भविष्य के लिए कोड लिखने के मूल सिद्धांतों पर नजर डाल लें।
सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है।
आपने पहले भी सुना होगा; समय से पहले अनुकूलन सभी बुराइयों की जड़ है। खैर, कभी-कभी। सॉफ़्टवेयर लिखते समय, मैं इस बात पर दृढ़ विश्वास रखता हूँ:
जितना संभव हो सके उतना वर्णनात्मक ; आपको इरादों को इस तरह बताने की कोशिश करनी चाहिए जैसे कि आप कोई कहानी लिख रहे हों।
जितना संभव हो सके उतना इष्टतम ; जिसका अर्थ है कि आपको भाषा के मूल सिद्धांतों को जानना चाहिए और तदनुसार उन्हें लागू करना चाहिए।
आपके कोड को उद्देश्य बताना चाहिए, और इसका बहुत कुछ इस बात से संबंधित है कि आप विधियों और चरों का नामकरण किस प्रकार करते हैं।
int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great
केवल चर नाम से ही आप कार्यक्षमता का अनुमान लगा सकते हैं।
जबकि numItems
अमूर्त है, backPackItems
आपको अपेक्षित व्यवहार के बारे में बहुत कुछ बताता है।
या कहें कि आपके पास यह तरीका है:
List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; }
जहां तक कोड की बात है, यह कमोबेश ठीक दिखता है।
क्या हम बेहतर कर सकते हैं? निश्चित रूप से कर सकते हैं!
List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; }
Collections.emptyList()
पढ़ना new ArrayList<>(0);
की तुलना में अधिक वर्णनात्मक है।
कल्पना करें कि आप पहली बार उपरोक्त कोड पढ़ रहे हैं और गार्ड क्लॉज़ पर ठोकर खाते हैं जो जाँचता है कि क्या उपयोगकर्ता वास्तव में देशों का दौरा कर चुका है। साथ ही, कल्पना करें कि यह एक लंबी क्लास में दफन है, Collections.emptyList()
पढ़ना निश्चित रूप से new ArrayList<>(0)
से अधिक वर्णनात्मक है, आप यह भी सुनिश्चित कर रहे हैं कि यह अपरिवर्तनीय है और यह सुनिश्चित कर रहे हैं कि क्लाइंट कोड इसे संशोधित नहीं कर सकता है।
अपनी भाषा को जानें और उसी के अनुसार उसका उपयोग करें। यदि आपको double
आवश्यकता है, तो उसे Double
ऑब्जेक्ट में लपेटने की आवश्यकता नहीं है। यदि आपको वास्तव में केवल Array
आवश्यकता है, तो List
उपयोग करने के लिए भी यही बात लागू होती है।
यदि आप थ्रेड्स के बीच स्थिति साझा कर रहे हैं तो आपको स्ट्रिंग्स को StringBuilder
या StringBuffer
का उपयोग करके संयोजित करना चाहिए:
// don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); }
अपने डेटाबेस को इंडेक्स करने का तरीका जानें। बाधाओं का अनुमान लगाएँ और उसके अनुसार कैश करें। उपरोक्त सभी अनुकूलन हैं। ये ऐसे अनुकूलन हैं जिनके बारे में आपको पता होना चाहिए और पहले नागरिक के रूप में उन्हें लागू करना चाहिए।
मैं कुछ साल पहले पढ़ी गई एक हैक को कभी नहीं भूल पाऊंगा। सच कहा जाए तो लेखक ने जल्दी ही अपनी बात वापस ले ली, लेकिन इससे पता चलता है कि अच्छे इरादों से कितनी बुराई पैदा हो सकती है।
// do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } }
नरक से एक कचरा संग्रहकर्ता हैक!
आप मूल लेख में उपरोक्त कोड क्यों और कैसे काम करता है, इसके बारे में अधिक पढ़ सकते हैं और, हालांकि यह शोषण निश्चित रूप से दिलचस्प है, यह उन चीजों में से एक है जो आपको कभी नहीं करना चाहिए।
Thread.sleep(0)
का कोई उद्देश्य नहीं है
जब भाषा द्वारा प्रदान किए गए सभी डिफ़ॉल्ट अनुकूलन के साथ लिखने के बाद, आप किसी अड़चन में फंस जाते हैं, तभी कुछ ज़्यादा जटिल बनाने की शुरुआत करें। लेकिन ऊपर बताए गए मनगढ़ंत बातों से दूर रहें।
यदि सब कुछ करने के बाद भी कचरा संग्रहकर्ता उपकरण अभी भी प्रतिरोध कर रहा है, तो आप निम्नलिखित चीजें आजमा सकते हैं:
यदि आपकी सेवा इतनी विलंबता-संवेदनशील है कि आप GC की अनुमति नहीं दे सकते, तो "एप्सिलॉन GC" के साथ चलाएँ और GC से पूरी तरह बचें ।
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
यह स्पष्ट रूप से आपकी मेमोरी को तब तक बढ़ाएगा जब तक आपको OOM अपवाद नहीं मिल जाता, इसलिए या तो यह एक अल्पकालिक परिदृश्य है या आपका प्रोग्राम ऑब्जेक्ट्स न बनाने के लिए अनुकूलित है
यदि आपकी सेवा कुछ हद तक विलंबता के प्रति संवेदनशील है, लेकिन स्वीकृत सहनशीलता कुछ छूट देती है , तो GC1 चलाएं और इसे कुछ इस प्रकार फीड करें -XX:MaxGCPauseTimeMillis=100
(डिफ़ॉल्ट 250ms है)
यदि समस्या बाहरी लाइब्रेरीज़ से उत्पन्न होती है , मान लीजिए कि उनमें से कोई System.gc()
या Runtime.getRuntime().gc()
को कॉल करता है जो स्टॉप-द-वर्ल्ड कचरा संग्रहकर्ता हैं, तो आप -XX:+DisableExplicitGC
के साथ चलाकर आपत्तिजनक व्यवहार को ओवरराइड कर सकते हैं
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
। आप यह JDK 21 GC बेंचमार्क भी देखना चाह सकते हैं।
संस्करण प्रारंभ | संस्करण समाप्त | डिफ़ॉल्ट जीसी |
---|---|---|
जावा 1 | जावा 4 | सीरियल कचरा संग्रहकर्ता |
जावा 5 | जावा 8 | समानांतर कचरा संग्रहकर्ता |
जावा 9 | चल रहे | G1 कचरा संग्रहकर्ता |
नोट 1: जावा 15 के बाद से, ZGC
उत्पादन के लिए तैयार है, लेकिन आपको अभी भी इसे -XX:+UseZGC
के साथ स्पष्ट रूप से सक्रिय करना होगा।
नोट 2: यदि VM दो से अधिक प्रोसेसर और 1792 MB से बड़ा या बराबर हीप आकार का पता लगाता है, तो VM मशीनों को सर्वर-क्लास मानता है। यदि सर्वर-क्लास नहीं है, तो यह डिफ़ॉल्ट रूप से Serial GC पर चला जाएगा ।
संक्षेप में, GC ट्यूनिंग का विकल्प तब चुनें जब यह स्पष्ट हो कि एप्लिकेशन की प्रदर्शन बाधाएँ सीधे कचरा संग्रहण व्यवहार से जुड़ी हैं और आपके पास सूचित समायोजन करने के लिए आवश्यक विशेषज्ञता है। अन्यथा, JVM की डिफ़ॉल्ट सेटिंग्स पर भरोसा करें और एप्लिकेशन-स्तरीय कोड को अनुकूलित करने पर ध्यान केंद्रित करें।
u/shiphe - आप पूरी टिप्पणी पढ़ना चाहेंगे
अगर आप बिना किसी वास्तविक बेंचमार्किंग के भावना के आधार पर अनुकूलन कर रहे हैं, तो आप खुद को नुकसान पहुंचा रहे हैं। JMH आपके एल्गोरिदम के प्रदर्शन का परीक्षण करने के लिए वास्तविक जावा लाइब्रेरी है। इसका उपयोग करें।
किसी प्रक्रिया को किसी विशिष्ट कोर पर पिन करने से कैश हिट में सुधार हो सकता है। यह अंतर्निहित हार्डवेयर और आपके रूटीन द्वारा डेटा से निपटने के तरीके पर निर्भर करेगा। फिर भी, यह लाइब्रेरी इसे लागू करना इतना आसान बनाती है कि, यदि कोई CPU-गहन विधि आपको खींच रही है, तो आप इसका परीक्षण करना चाहेंगे।
यह उन पुस्तकालयों में से एक है, जिसकी आपको ज़रूरत न होने पर भी आप अध्ययन करना चाहेंगे। विचार अल्ट्रा-लो लेटेंसी समवर्तीता की अनुमति देना है। लेकिन जिस तरह से इसे लागू किया गया है, मैकेनिकल सहानुभूति से लेकर रिंग बफर तक, बहुत सारी नई अवधारणाएँ लाता है। मुझे अभी भी याद है जब मैंने इसे पहली बार खोजा था, सात साल पहले, इसे पचाने के लिए पूरी रात काम किया था।
jvmquake
का आधार यह है कि जब JVM के साथ चीजें गलत दिशा में जाती हैं, तो आप चाहते हैं कि यह मर जाए और लटके नहीं। कुछ साल पहले, मैं HTCondor क्लस्टर पर सिमुलेशन चला रहा था जो कि बहुत सीमित मेमोरी पर था, और कभी-कभी, "आउट ऑफ मेमोरी" त्रुटियों के कारण जॉब अटक जाती थी।
यह लाइब्रेरी JVM को बंद करने के लिए मजबूर करती है, जिससे आप वास्तविक त्रुटि से निपट सकते हैं। इस विशिष्ट मामले में, HTCondor स्वचालित रूप से कार्य को पुनः शेड्यूल करेगा।
जिस कोड की वजह से मुझे यह पोस्ट लिखने पर मजबूर होना पड़ा? मैंने इससे भी बदतर कोड लिखा है। मैं अभी भी ऐसा ही करता हूँ। हम यही उम्मीद कर सकते हैं कि हम लगातार कम गड़बड़ियाँ करें।
मुझे उम्मीद है कि कुछ वर्षों बाद मैं अपना कोड देखकर असंतुष्ट हो जाऊंगा।
और यह एक अच्छा संकेत है।
visitedCountries()
में परिवर्तनशीलता त्रुटि को पकड़ने और विस्तृत स्पष्टीकरण के लिए फ्लोरियनशैट्ज़ को धन्यवाद।Wasteofserver.com पर भी प्रकाशित