
एप्लिकेशन क्लास डेटा शेयरिंग (ऐपसीडीएस) - स्टार्टअप को गति देने और मेमोरी को बचाने के लिए जेवीएम सुविधा। JDK 1.5 (2004) में हॉटस्पॉट में अपनी प्रारंभिक अवस्था में दिखाई देने के बाद , लंबे समय तक यह बहुत सीमित रहा, और आंशिक रूप से व्यावसायिक भी रहा। केवल OpenJDK 10 (2018) के साथ इसे मात्र नश्वर लोगों के लिए उपलब्ध कराया गया था, साथ ही साथ इस क्षेत्र का विस्तार भी किया गया था। और हाल ही में जारी जावा 13 ने इस एप्लिकेशन को सरल बनाने की कोशिश की।
AppCDS का विचार एक ही मेजबान पर एक ही JVM के उदाहरणों के बीच भरी हुई कक्षाओं को एक बार "साझा" करना है। ऐसा लगता है कि यह हजारों पुस्तकालय कक्षाओं के साथ माइक्रोसाफ्टर्स के लिए विशेष रूप से स्प्रिंग बूट पर "ब्रॉयलर" के लिए बहुत अच्छा होना चाहिए, क्योंकि अब इन कक्षाओं को प्रत्येक जेवीएम उदाहरण की प्रत्येक शुरुआत में लोड (पार्स और सत्यापित) करने की आवश्यकता नहीं होगी, और उन्हें मेमोरी में डुप्लिकेट नहीं किया जाएगा। इसका मतलब है कि लॉन्च तेजी से होना चाहिए, और मेमोरी की खपत कम होनी चाहिए। कमाल है, है ना?
सब कुछ ऐसा है, सब कुछ ऐसा है। लेकिन अगर आप, ओडनोकैब्रायनैन, बुलेवार्ड संकेतों में नहीं, बल्कि विशिष्ट संख्या और उदाहरणों में विश्वास करते थे, तो कैट में आपका स्वागत है - आइए जानने की कोशिश करें कि यह वास्तव में कैसा है ...
डिस्क्लेमर के बजाय
इससे पहले कि आप AppCDS का उपयोग करने के लिए एक गाइड नहीं है, लेकिन एक छोटे से अध्ययन के परिणामों का सारांश है। मुझे यह समझने में दिलचस्पी थी कि यह JVM फ़ंक्शन मेरे काम करने की परियोजना में कैसे लागू होता है, और मैंने इस लेख में परिणाम की स्थापना करते हुए, एक उद्यम डेवलपर के दृष्टिकोण से इसका मूल्यांकन करने की कोशिश की। इसमें मॉड्यूल-पथ पर AppCDS का उपयोग करने, अन्य आभासी मशीनों (नहीं HotSpot) पर AppCDS को लागू करने और कंटेनरों का उपयोग करने की जटिलताओं जैसे विषय शामिल नहीं थे। लेकिन विषय की खोज के लिए एक सैद्धांतिक हिस्सा है, साथ ही एक प्रयोगात्मक भाग भी लिखा गया है ताकि आप स्वयं अनुभव को दोहरा सकें। कोई भी परिणाम अभी तक उत्पादन में लागू नहीं हुआ है, लेकिन कौन जानता है कि कल क्या होगा ...
सिद्धांत
AppCDS के लिए एक संक्षिप्त परिचय
इस विषय से परिचित होना आपके लिए कई स्रोतों में हो सकता है, उदाहरण के लिए:
- निकोलाई तोलोग के एक लेख में (जावा 13 बन्स सहित, लेकिन स्प्रिंग बूट के बिना)
- वोल्कर सिमनिस (जावा 13 के बिना, लेकिन विवरण के साथ) एक रिपोर्ट और लेख में
- इन पंक्तियों के लेखक की एक रिपोर्ट में (जावा 13 के बिना, लेकिन स्प्रिंग बूट पर जोर देने के साथ)
रिटेलिंग में शामिल नहीं होने के लिए, मैं केवल कुछ बिंदुओं को उजागर करूंगा जो इस लेख के लिए महत्वपूर्ण हैं।
सबसे पहले, AppCDS सीडीएस सुविधा का एक विस्तार है जो लंबे समय तक हॉटस्पॉट में दिखाई देता है, जिसका सार इस प्रकार है:

दोनों विचारों को जीवन में लाने के लिए, आपको निम्नलिखित (सामान्य शब्दों में) करने की आवश्यकता है:
- उन कक्षाओं की सूची प्राप्त करें जिन्हें आप एप्लिकेशन इंस्टेंस के बीच साझा करना चाहते हैं
- मेमोरी मैपिंग के लिए उपयुक्त एक संग्रह में इन वर्गों को मिलाएं
- स्टार्टअप पर एप्लिकेशन के प्रत्येक उदाहरण के लिए संग्रह को कनेक्ट करें
ऐसा लगता है कि एल्गोरिथ्म सिर्फ 3 चरण है - इसे लें और इसे करें। लेकिन यहां खबर शुरू होती है, सभी तरह की चीजों की।
बुरी बात यह है कि सबसे खराब स्थिति में, इनमें से प्रत्येक आइटम अपने स्वयं के विशिष्ट विकल्पों के साथ कम से कम एक जेवीएम लॉन्च में बदल जाता है, जिसका अर्थ है कि संपूर्ण एल्गोरिथ्म एक ही प्रकार के विकल्पों और फाइलों की सूक्ष्म बाजीगरी है। यह बहुत आशाजनक ध्वनि नहीं है, है ना?
लेकिन अच्छी खबर है: इस एल्गोरिथ्म में सुधार पर काम जारी है , और जावा के प्रत्येक रिलीज के साथ, इसका आवेदन आसान हो जाता है। उदाहरण के लिए:
- OpenJDK 10 और 11 में, आप चरण 1 को छोड़ सकते हैं यदि आप केवल मुख्य JDK कक्षाएं साझा करना चाहते हैं, क्योंकि वे पहले से ही हमारे लिए संकलित किए गए हैं और
$JAVA_HOME\lib\classlist
(≈1200 पीसी) में डाल दिए गए हैं। - OpenJDK 12 में, आप चरण 2 को छोड़ सकते हैं, क्योंकि कक्षाओं की सूची के साथ, वितरण संग्रह में उनके साथ एक तैयार संग्रह भी शामिल है, जिसका उपयोग बॉक्स से बाहर किया जाता है और एक स्पष्ट कनेक्शन की आवश्यकता नहीं होती है।
- मामले में आप और सब कुछ साझा करना चाहते हैं (और आमतौर पर बस चाहते हैं)
OpenJDK 13 डायनामिक सीडीएस अभिलेखागार प्रदान करता है - अभिलेखागार जो कि आवेदन के संचालन के दौरान एकत्र किया जाता है और इसे सेवित होने पर सहेजा जाता है। यह आपको अंक 1 और 2 को एक नहीं बहुत अधिक भ्रमित बिंदु में बदलने की अनुमति देता है (हालांकि बाद में सब कुछ इतना सरल नहीं है, लेकिन अधिक है)।
इस प्रकार, कोई फर्क नहीं पड़ता कि AppCDS तैयार करने की प्रक्रिया क्या है, ऊपर सूचीबद्ध 3 चरण हमेशा इसके पीछे होते हैं, बस कुछ मामलों में वे घूंघट होते हैं।
जैसा कि आपने शायद देखा, AppCDS के आगमन के साथ, कई एप्लिकेशन क्लासेस एक दोहरी जिंदगी शुरू करते हैं: वे एक साथ अपने पूर्व स्थानों में रहते हैं (सबसे अधिक बार JAR फाइलें) और एक नए साझा संग्रह में। उसी समय, डेवलपर उन्हें उसी स्थान पर बदल / हटा / पूरक करता रहता है, और JVM काम करते समय उन्हें नए से लेता है। ऐसी स्थिति के खतरे को देखने के लिए किसी को एक दिव्यांग होने की आवश्यकता नहीं है: यदि कुछ भी नहीं किया जाता है, तो जल्दी या बाद में कक्षाओं की प्रतियां प्रस्फुटित होंगी, और हमें विशिष्ट "जार नरक" के कई आकर्षण मिलेंगे। यह स्पष्ट है कि जेवीएम वर्ग परिवर्तनों को रोक नहीं सकता है, लेकिन यह समय में एक विसंगति का पता लगाने में सक्षम होना चाहिए। हालाँकि, युग्मों की तुलना वर्गों द्वारा करना, चेकसम द्वारा भी, एक विचार है; यह बाकी उत्पादकता लाभ को नकार सकता है। संभवत: इसीलिए जेवीएम के इंजीनियरों ने तुलनात्मक वस्तु के रूप में अलग-अलग वर्गों का चयन नहीं किया, लेकिन पूरे क्लासपाथ, और ऐपसीडीएस दस्तावेज में कहा गया था: "एक साझा संग्रह बनाते समय क्लासपैथ एक ही होना चाहिए (या कम से कम एक उपसर्ग जैसा कि आवेदन के बाद के लॉन्च के साथ होना चाहिए।"
ध्यान दें कि आर्काइव निर्माण के समय इस्तेमाल किया जाने वाला क्लासपाथ रन टाइम में इस्तेमाल किए जाने वाले क्लासपाथ के समान (या उपसर्ग) होना चाहिए।
लेकिन यह एक असंदिग्ध कथन नहीं है, क्योंकि, जैसा कि आप याद करते हैं, एक क्लासपैथ विभिन्न तरीकों से बनाया जा सकता है, जैसे:
- संकलित पैकेज निर्देशिकाओं से नंगे
.class
फ़ाइलों को पढ़ना,
उदा। java com.example.Main
- वाइल्डकार्ड का उपयोग करते समय JAR फ़ाइलों के साथ स्कैनिंग निर्देशिकाएं,
जैसे java -cp mydir/* com.example.Main
- JAR और / या ज़िप फ़ाइलों की स्पष्ट सूची,
जैसे java -cp lib1.jar;lib2.jar com.example.Main
(यह इस तथ्य का उल्लेख नहीं है कि -cp/-classpath/--class-path
अलग-अलग तरीके से भी सेट किया जा सकता है, उदाहरण के लिए, जेवीएम विकल्प -cp/-classpath/--class-path
, CLASSPATH
पर्यावरण चर या लॉन्च किए जाने वाले Class-Path
JAR फ़ाइल की विशेषता)
इन विधियों में से, AppCDS में केवल एक का समर्थन किया जाता है - JAR फ़ाइलों की स्पष्ट गणना। जाहिरा तौर पर, हॉटस्पॉट JVM इंजीनियरों ने महसूस किया कि AppCDS संग्रह में क्लासपैथ की तुलना करना और लॉन्च किए गए एप्लिकेशन में पर्याप्त तेज़ और विश्वसनीय होगा, यदि वे स्पष्ट रूप से यथासंभव स्पष्ट रूप से निर्दिष्ट किए गए हों - एक सामान्य संपूर्ण सूची के साथ।
CDS / AppCDS केवल JAR फ़ाइलों से संग्रह कक्षाओं का समर्थन करता है।
यहां यह नोट करना महत्वपूर्ण है कि यह कथन पुनरावर्ती नहीं है, अर्थात्। JAR फ़ाइलों के अंदर JAR फ़ाइलों पर लागू नहीं होता है (जब तक कि यह डायनेमिक सीडीएस के बारे में नहीं है, नीचे देखें)। इसका अर्थ है कि स्प्रिंग बूट द्वारा जारी की गई सामान्य JAR-डॉल्स सिर्फ उसी तरह काम नहीं करेंगी जैसे कि नियमित AppCDS के साथ, आपको बैठना होगा।
सीडीएस के काम में एक और पकड़ यह है कि साझा अभिलेखागार को निश्चित पते के साथ मेमोरी पर प्रोजेक्ट किया जाता है (आमतौर पर 0x800000000
पर शुरू होता है)। यह अपने आप में खराब नहीं है, लेकिन चूंकि एड्रेस स्पेस लेआउट रैंडमाइजेशन (एएसएलआर) अधिकांश ऑपरेटिंग सिस्टम पर डिफ़ॉल्ट रूप से सक्षम है, इसलिए आवश्यक मेमोरी रेंज आंशिक रूप से कब्जा की जा सकती है। हॉटस्पॉट JVM इस मामले में क्या करता है विशेष विकल्प है -Xshare
जो तीन मूल्यों का समर्थन करता है:
-Xshare:on
- बल CDS / AppCDS; यदि सीमा व्यस्त है, तो JVM एक त्रुटि के साथ बाहर निकलता है। उत्पादन में उपयोग के लिए इस मोड की अनुशंसा नहीं की जाती है , क्योंकि इससे एप्लिकेशन लॉन्च करते समय छिटपुट दुर्घटना हो सकती है।-Xshare:off
- (आप) स्विच सीडीएस / AppCDS; साझा डेटा का उपयोग पूरी तरह से अक्षम करता है (एम्बेडेड अभिलेखागार सहित)-Xshare:auto
- जब आवश्यक मेमोरी रेंज आवंटित करने के लिए असंभवता के मामले में, JVM का डिफ़ॉल्ट व्यवहार, चुपचाप आत्मसमर्पण करता है और हमेशा की तरह कक्षाओं को लोड करता है
इस लेख को लिखने के समय, ओरेकल इस तरह की समस्याओं को दूर करने के लिए काम कर रहा है, लेकिन एक रिलीज़ नंबर अभी तक असाइन नहीं किया गया है।
ये विकल्प बाद में आंशिक रूप से हमारे लिए उपयोगी हैं, लेकिन अब हम इसे देखते हैं ...
AppCDS अनुप्रयोग
AppCDS के साथ कई तरीके हैं। अपना जीवन बर्बाद कर लो माइक्रोसर्विस के काम का अनुकूलन करें। वे जटिलता और संभावित लाभ में बहुत भिन्न होते हैं, इसलिए तुरंत यह तय करना महत्वपूर्ण है कि किस पर बाद में चर्चा की जाएगी।
सबसे सरल भी AppCDS का उपयोग नहीं है, लेकिन सिर्फ सीडीएस - यह तब होता है जब केवल प्लेटफ़ॉर्म वर्ग साझा संग्रह में आते हैं (देखें "ए संक्षिप्त परिचय AppCDS के लिए")। हम इस विकल्प को तुरंत हटा देंगे, क्योंकि स्प्रिंग बूट पर माइक्रोसर्विस पर लागू होने पर यह बहुत कम लाभ देता है। यह उनके वितरण में साझा वर्गों की संख्या के अनुपात से देखा जा सकता है, जो कि एक वास्तविक माइक्रोसैस सर्विस (ग्रीन सेगमेंट देखें) के उदाहरण का उपयोग करते हैं:

अधिक जटिल, लेकिन होनहार पूर्ण-पूर्ण AppCDS का उपयोग है, अर्थात्, एक ही संग्रह में पुस्तकालय और अनुप्रयोग कक्षाओं दोनों का समावेश। यह विकल्पों का एक पूरा परिवार है जो भाग लेने वाले अनुप्रयोगों की संख्या और उदाहरणों की संख्या के संयोजन से लिया गया है। AppCDS के विभिन्न अनुप्रयोगों के लाभ और कठिनाइयों के व्यक्तिपरक लेखक के आकलन निम्नलिखित हैं।
ध्यान दें:
- एक उदाहरण (नंबर 1) में एक आवेदन करने के लिए, स्मृति लाभ शून्य या यहां तक कि नकारात्मक हो सकता है (विशेषकर जब आपके खाते में )
- सही साझा किए गए संग्रह को बनाने के लिए कार्रवाइयों की आवश्यकता होती है, जिनमें से जटिलता इस बात पर निर्भर नहीं करती है कि आवेदन कितनी प्रतियों में लॉन्च किया जाएगा (तुलना नं 1-2 और नंबर 3-4 की जोड़ी)
- इसी समय, एक उदाहरण से कई में संक्रमण, स्पष्ट रूप से, दोनों संकेतकों के लिए लाभ में वृद्धि देता है, लेकिन तैयारी की जटिलता को प्रभावित नहीं करता है।
इस लेख में, हम केवल विकल्प नंबर 2 (नंबर 1 के माध्यम से) तक पहुंचेंगे , क्योंकि यह ऐपसीडीएस के साथ एक करीबी परिचित के लिए काफी सरल है और केवल अतिरिक्त चाल के बिना हम हाल ही में जारी किए गए जेईपी -350 डायनेमिक सीडीएस अभिलेखागार का उपयोग कर सकते हैं, जो कार्रवाई में महसूस करना चाहते हैं।
गतिशील सीडीएस अभिलेखागार
JEP-350 डायनामिक सीडीएस आर्काइव्स, जो जावा 13 के प्रमुख नवाचारों में से एक है, को AppCDS के उपयोग को सरल बनाने के लिए डिज़ाइन किया गया है। सरलीकरण महसूस करने के लिए, आपको पहले जटिलता को समझना होगा। आपको याद दिला दूं कि AppCDS लगाने के लिए क्लासिक, "क्लीन" एल्गोरिदम में 3 चरण होते हैं: (1) साझा वर्गों की एक सूची प्राप्त करें, (2) उनसे एक संग्रह बनाएं, और (3) जुड़े हुए संग्रह के साथ एप्लिकेशन को चलाएं। इन चरणों में से, केवल 3 वास्तव में उपयोगी है, बाकी केवल इसके लिए तैयारी है। और यद्यपि वर्गों की एक सूची प्राप्त करना (चरण # 1) बहुत सरल लग सकता है (कुछ मामलों में यह आवश्यक भी नहीं है), वास्तव में जब गैर-तुच्छ अनुप्रयोगों के साथ काम करते हैं, तो यह सबसे कठिन हो जाता है, खासकर स्प्रिंग बूट के संबंध में। तो इस कदम को खत्म करने के लिए, या इसे स्वचालित करने के लिए, JEP-350 की आवश्यकता है। यह विचार यह है कि JVM स्वयं उन वर्गों की एक सूची तैयार करता है जिन्हें आवेदन की आवश्यकता है, और फिर स्वयं उनसे तथाकथित "गतिशील" संग्रह बनाता है। सहमत हूं, यह अच्छा लगता है। लेकिन पकड़ यह है कि अब यह स्पष्ट नहीं हो जाता है कि संचित वर्गों को रोकना और उन्हें संग्रह में रखने के लिए आगे बढ़ना है। इससे पहले, क्लासिक AppCDS में, हमने इस क्षण को खुद चुना था और यहां तक कि इन कार्यों के बीच एक संग्रह में बदलने से पहले कक्षाओं की सूची में कुछ बदलने के लिए शपथ ले सकते हैं। अब यह स्वचालित रूप से और केवल एक पल में हो रहा है, जिसके लिए जेवीएम इंजीनियरों ने चुना है, शायद, एकमात्र समझौता विकल्प - जेवीएम का नियमित बंद। इसका मतलब यह है कि संग्रह तब तक नहीं बनाया जाएगा जब तक कि आवेदन बंद न हो जाए। इस समाधान के कुछ महत्वपूर्ण परिणाम हैं:
- JVM क्रैश होने की स्थिति में, आर्काइव नहीं बनाया जाएगा, तब तक संचित कक्षाओं की सूची कितनी अद्भुत होगी (आप बाद में नियमित साधनों का उपयोग करके इसे नहीं निकाल सकते हैं)।
- संग्रह केवल उन वर्गों से बनाया जाएगा जो आवेदन सत्र के दौरान लोड करने में कामयाब रहे। वेब अनुप्रयोगों के लिए, इसका मतलब है कि शुरू करने और वहीं रोककर एक संग्रह बनाना सही नहीं है, क्योंकि तब से कई महत्वपूर्ण कक्षाएं संग्रह में नहीं आएंगी। आवेदन के लिए कम से कम एक HTTP अनुरोध निष्पादित करना आवश्यक है (और इसे सभी परिदृश्यों में ठीक से चलाने के लिए बेहतर है) ताकि सभी वर्ग जो इसे वास्तव में उपयोग करते हैं, लोड हो जाए।
गतिशील और स्थिर अभिलेखागार के बीच एक महत्वपूर्ण अंतर यह है कि वे हमेशा बुनियादी स्थैतिक अभिलेखागार पर एक "ऐड-ऑन" का गठन करते हैं, जो या तो जावा वितरण किट में निर्मित या क्लासिक 3-चरण तरीके से अलग से बनाया जा सकता है।
गतिशील रूप से, गतिशील सीडीएस अभिलेखागार का उपयोग करते हुए दो विकल्पों के साथ दो जेवीएम लॉन्च के लिए नीचे आता है:
-XX:ArchiveClassesAtExit=archive.jsa
विकल्प के साथ ट्रायल रन -XX:ArchiveClassesAtExit=archive.jsa
, जिसके अंत में एक डायनामिक आर्काइव बनाया जाएगा (आप किसी भी पथ और नाम को निर्दिष्ट कर सकते हैं)-XX:SharedArchiveFile=archive.jsa
विकल्प के साथ उपयोगी लॉन्च -XX:SharedArchiveFile=archive.jsa
, जो पहले बनाए गए संग्रह का उपयोग करेगा
दूसरा विकल्प नियमित स्थिर संग्रह को जोड़ने से अलग नहीं है। लेकिन अगर अचानक मूल स्थैतिक संग्रह डिफ़ॉल्ट स्थान (जेडीके के अंदर) में नहीं है, तो इस विकल्प में इसके लिए पथ का संकेत भी शामिल हो सकता है, उदाहरण के लिए:
-XX:SharedArchiveFile=base.jsa:dynamic.jsa
(Windows के तहत, पथ विभाजक "?" वर्ण होना चाहिए)
अब आप AppCDS के बारे में पर्याप्त जानते हैं ताकि आप इसे कार्रवाई में देख सकें।
अभ्यास
प्रायोगिक खरगोश
इसलिए कि व्यवहार में AppCDS का हमारा अनुप्रयोग एक विशिष्ट HelloWorld तक सीमित नहीं है, हम स्प्रिंग बूट पर वास्तविक अनुप्रयोग के आधार के रूप में लेंगे। मेरे सहयोगियों और मुझे अक्सर दूरस्थ परीक्षण सर्वर पर एप्लिकेशन लॉग देखना पड़ता है, और "लाइव" देखना होता है, जैसे वे लिखे जाते हैं। इसका उपयोग करने के लिए एक पूर्ण लॉग लॉग एग्रीगेटर (ईएलके की तरह) अक्सर उपयुक्त नहीं होता है; लॉग फ़ाइलों को अंतहीन रूप से डाउनलोड करना - एक लंबे समय के लिए, और tail
के ग्रे कंसोल आउटपुट को देखना निराशाजनक है। इसलिए, मैंने एक वेब एप्लिकेशन बनाया जो वास्तविक समय में किसी भी लॉग को सीधे ब्राउज़र में आउटपुट कर सकता है, महत्व स्तर (एक ही समय XML को स्वरूपित करके) लाइनों को रंगीन कर सकता है, कई लॉग को एक में एकत्र कर सकता है, साथ ही साथ अन्य ट्रिक्स भी। इसे ANALOG कहा जाता है (जैसे "लॉग विश्लेषक", हालांकि यह सच नहीं है) और GitHub पर स्थित है । विस्तार करने के लिए स्क्रीनशॉट पर क्लिक करें:

तकनीकी रूप से, यह स्प्रिंग बूट + स्प्रिंग इंटीग्रेशन पर एक एप्लीकेशन है, जिसके हुड के नीचे, tail
, kubectl
और kubectl
(फ़ाइलों से लॉग का समर्थन करने के लिए, डॉक कंटेनर और कुबेरनेट्स संसाधन, क्रमशः)। यह क्लासिक "मोटी" स्प्रिंग बूट जार फ़ाइल के रूप में आता है। रनटाइम में, अनुप्रयोग मेमोरी में K10K कक्षाएं लटकी हुई हैं , जिनमें से अधिकांश स्प्रिंग और जेडीके कक्षाएं हैं । जाहिर है, ये कक्षाएं बहुत कम ही बदलती हैं, जिसका अर्थ है कि उन्हें एक साझा संग्रह में रखा जा सकता है और आवेदन के सभी उदाहरणों में पुन: उपयोग किया जा सकता है, स्मृति और सीपीयू की बचत।
एकल प्रयोग
चलिए अब डायनामिक AppCDS के मौजूदा ज्ञान को प्रयोगात्मक खरगोश पर लागू करते हैं। चूंकि सब कुछ तुलना में जाना जाता है, हमें कुछ संदर्भ बिंदु की आवश्यकता होगी - कार्यक्रम की स्थिति जिसके साथ हम प्रयोग के दौरान प्राप्त परिणामों की तुलना करेंगे।
परिचयात्मक टिप्पणी
- आगे सभी कमांड लिनक्स के लिए हैं। विंडोज और मैकओएस के लिए अंतर मौलिक नहीं हैं।
- जेआईटी संकलन परिणामों को स्पष्ट रूप से प्रभावित कर सकता है और, सिद्धांत रूप में, प्रयोग की शुद्धता के लिए, इसे बंद कर दिया जा सकता है (
-Xint
विकल्प के साथ, जैसा कि उल्लेख लेख में किया गया था), लेकिन अधिकतम plauses के लिए यह नहीं करने का निर्णय लिया गया था। - शुरुआती समय के बारे में निम्नलिखित संख्याएं एक तेज परीक्षण सर्वर पर प्राप्त की गई थीं। कामकाजी मशीनों पर, समान संख्या, एक नियम के रूप में, अधिक विनम्र हैं, लेकिन चूंकि हम पूर्ण मूल्यों में रुचि नहीं रखते हैं, लेकिन प्रतिशत वृद्धि में, हम इस अंतर को महत्वहीन मानते हैं।
- समय से पहले साझा की गई स्मृति को मापने की जटिलता में नहीं जाने के लिए, अब हम बाइट्स में सटीक रीडिंग प्राप्त करना छोड़ देंगे। इसके बजाय, हम " सीडीएस क्षमता " की अवधारणा को पेश करते हैं , साझा कक्षाओं की संख्या का प्रतिशत लोड की गई कक्षाओं की कुल संख्या के रूप में व्यक्त करते हैं। यह, निश्चित रूप से, एक अमूर्त मात्रा है, लेकिन दूसरी ओर, यह सीधे वास्तविक मेमोरी खपत को प्रभावित करता है; इसके अलावा, इसकी परिभाषा ओएस पर बिल्कुल भी निर्भर नहीं करती है, और इसकी गणना के लिए, केवल लॉग पर्याप्त हैं।
संदर्भ बिंदु
इस बिंदु को नए सिरे से डाउनलोड किए गए एप्लिकेशन की स्थिति होने दें, अर्थात किसी भी AppCDS'ov और अन्य के स्पष्ट उपयोग के बिना। इसका मूल्यांकन करने के लिए, हमें चाहिए:
OpenJDK 13 स्थापित करें (उदाहरण के लिए, घरेलू लाइबेरिका वितरण, लेकिन लाइट संस्करण नहीं)।
उदाहरण के लिए, इसे PATH पर्यावरण चर या JAVA_HOME
भी जोड़ना होगा:
export JAVA_HOME=~/tools/jdk-13
डाउनलोड ANALOG (लेखन के समय, नवीनतम संस्करण v0.12.1 था)।
यदि आवश्यक हो, तो आप config/application.yaml
कर सकते हैं config/application.yaml
फ़ाइल में server.address
पैरामीटर अनुप्रयोग को एक्सेस करने के लिए बाहरी होस्ट नाम (डिफ़ॉल्ट रूप से, server.address
निर्दिष्ट किया गया है)।
JVM वर्ग लोड लॉगिंग सक्षम करें।
ऐसा करने के लिए, आप इस मान के साथ JAVA_OPTS
पर्यावरण चर को कॉक कर सकते हैं:
export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log
यह विकल्प JVM को दिया जाएगा और इसे प्रत्येक वर्ग के स्रोत को गिरवी रखने के लिए कहेगा ।
एक परीक्षण चलाएं:
bin/analog
स्क्रिप्ट के साथ एप्लिकेशन को चलाएं- Open http: // localhost: 8083 ब्राउज़र में, पोक बटन और डौस
bin/analog
स्क्रिप्ट कंसोल में Ctrl+C
दबाकर एप्लिकेशन को रोकें
परिणाम ( log/
निर्देशिका में फ़ाइलों से) लें
भरी हुई कक्षाओं की कुल संख्या ( class-load.log
):
cat class-load.log | wc -l 10463
उनमें से कितने साझा संग्रह से डाउनलोड किए गए हैं (इसके अनुसार):
grep -o 'source: shared' - class-load.log 1146
औसत प्रारंभ समय (शुरुआत की एक श्रृंखला के बाद; analog.log
द्वारा):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225
तो, इस कदम पर, सीडीएस की क्षमता 1146/10463=0,1095
0.1095 %11% थी । यदि आप आश्चर्यचकित हैं कि साझा कक्षाएं कहां से आई हैं (आखिरकार, हमने अभी तक किसी भी AppCDS को शामिल नहीं किया है), तो मैं आपको याद दिलाता हूं कि 12 वें संस्करण से शुरू होने वाले, JDK में समाप्त CDS संग्रह $JAVA_HOME/lib/server/classes.jsa
, बिल्ट कक्षाओं की तैयार सूची से कम नहीं:
cat $JAVA_HOME/lib/classlist | wc -l 1170
अब, आवेदन की प्रारंभिक स्थिति का आकलन करने के बाद, हम इसके लिए AppCDS लागू कर सकते हैं और तुलना करके, यह समझते हैं कि यह क्या देता है।
कोर अनुभव
जैसा कि डायनेमिक AppCDS आर्काइव बनाने के लिए प्रलेखन के लिए हमें दिया गया है, आपको -XX:ArchiveClassesAtExit
विकल्प के साथ एप्लिकेशन के केवल एक ट्रायल रन को करने की -XX:ArchiveClassesAtExit
है -XX:ArchiveClassesAtExit
। अगले लॉन्च से, संग्रह का उपयोग किया जा सकता है और लाभ प्राप्त कर सकते हैं। इसे उसी प्रायोगिक खरगोश (AnaLog) पर सत्यापित करने के लिए, आपको चाहिए:
रन कमांड में निर्दिष्ट विकल्प जोड़ें:
export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa"
लॉगिंग बढ़ाएँ:
export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log"
यह विकल्प अनुप्रयोग बंद होने पर सीडीएस संग्रह बनाने की लॉगिंग प्रक्रिया को बाध्य करेगा।
संदर्भ बिंदु के साथ समान परीक्षण चलाएं:
bin/analog
स्क्रिप्ट के साथ एप्लिकेशन को चलाएं- Open http: // localhost: 8083 ब्राउज़र में, पोक बटन और डौस
bin/analog
स्क्रिप्ट कंसोल में Ctrl+C
दबाकर एप्लिकेशन को रोकें
उसके बाद, सभी प्रकार की चेतावनी के साथ एक जबरदस्त फुटक्लॉथ कंसोल में गिरना चाहिए, और log/cds.log
को विवरण से भरना चाहिए; वे हमें अभी तक ब्याज नहीं देते हैं
परीक्षण मोड को उपयोगी से लॉन्च मोड पर स्विच करें:
export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log"
यहां हम JAVA_OPTS
चर को पूरक नहीं करते हैं, लेकिन नए मूल्यों के साथ इसे फिर से मिटा देते हैं, जिसमें (1) एक साझा संग्रह का उपयोग करना शामिल है, (2) लॉगिंग क्लास स्रोत और (3) लॉगिंग क्लास-पाथ चेक।
अनुच्छेद 3 से योजना के अनुसार आवेदन का एक उपयोगी लॉन्च करें।
परिणाम ( log/
निर्देशिका में फ़ाइलों से) लें
यह सत्यापित करना कि AppCDS वास्तव में लागू ( class-path.log
):
[0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok
लाइनों के बाद के ok
type=BOOT
और type=APP
क्रमशः निर्मित और लागू सीडीएस अभिलेखागार के सफल उद्घाटन, सत्यापन और लोडिंग को इंगित करता है।
भरी हुई कक्षाओं की कुल संख्या ( class-load.log
):
cat class-load.log | wc -l 10403
उनमें से कितने साझा संग्रह से डाउनलोड किए गए हैं (इसके अनुसार):
grep -o 'source: shared' -c class-load.log 6910
औसत प्रारंभ समय (प्रारंभ की एक श्रृंखला के बाद? analog.log
फ़ाइल द्वारा):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167
लेकिन इस कदम पर, सीडीएस की क्षमता पहले से ही 6910/10403≈0,66
= 66% थी , अर्थात, यह संदर्भ बिंदु की तुलना में 55% की वृद्धि हुई। उसी समय, औसत लॉन्च समय (4,5225-4,04167)=0,48
सेकंड, अर्थात आरंभिक मूल्य के %10.6% से तेज है।
परिणाम विश्लेषण
आइटम का कार्य शीर्षक है: "इतना कम क्यों?"
हमने, निर्देशों के अनुसार सब कुछ किया, लेकिन संग्रह में सभी कक्षाएं नहीं थीं। उनकी संख्या प्रयोगकर्ता की मशीन की कंप्यूटिंग शक्ति की तुलना में लॉन्च के समय को प्रभावित करती है, इसलिए हम इस संख्या पर ध्यान केंद्रित करेंगे।
यदि आपको याद है, तो हमने परीक्षण रन के बाद प्रयोगात्मक एप्लिकेशन के बंद होने के दौरान बनाई गई log/cds.log
फ़ाइल को अनदेखा कर दिया। इस हॉटस्पॉट फ़ाइल में, JVM ने कृपया प्रत्येक वर्ग के लिए चेतावनी वर्ग का उल्लेख किया जो CDS संग्रह में नहीं दिखाई दिया। यहां ऐसे अंकों की कुल संख्या दी गई है:
grep -o '[warning]' cds.log -c 3591
यह देखते हुए कि class-load.log
लॉग लॉग में केवल 10K + वर्गों का उल्लेख किया गया है और उनमें से 66% संग्रह से डाउनलोड किए गए हैं, यह समझना मुश्किल नहीं है कि cds.log
में सूचीबद्ध 3600 वर्ग सीडीएस क्षमता के "लापता" 44% हैं। अब आपको यह पता लगाने की आवश्यकता है कि उन्हें क्यों छोड़ दिया गया था।
यदि आप cds.log लॉग को देखते हैं, तो यह पता चलता है कि वर्गों को छोड़ देने के केवल 4 अद्वितीय कारण हैं। यहाँ उनमें से प्रत्येक के उदाहरण हैं:
Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded
सभी 3591 छूटी हुई कक्षाओं में, ये कारण ऐसी आवृत्ति के साथ यहां पाए जाते हैं:

उन पर एक नज़र डालें:
Unsafe anonymous class
JVM “” , -, .
Not linked
, “” , , . , StackOverflow . , , “” () JAR- , AppCDS. , ( ).
Pre JDK 6 class
, CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).
Skipping ... : super class/interface ... is excluded
, “” . CDS', . उदाहरण के लिए:
[warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded
निष्कर्ष
CDS 100%.
, , , , , . .
JEP-310 , AppCDS JDK. . , . CDS (, , ) .
प्रयोगात्मक खरगोश को क्लोन करने के लिए (कई उदाहरणों में AnaLog चलाएं), हमें सेटिंग्स में कुछ बदलने की आवश्यकता है; यह उठा प्रक्रियाओं "कोहनी" के लिए नहीं की अनुमति देगा। स्प्रिंग बूट के लिए धन्यवाद, आप इसे किसी भी फाइल को संपादित या कॉपी किए बिना कर सकते हैं; JVM विकल्पों के द्वारा किसी भी सेटिंग को ओवरराइड किया जा सकता है। पर्यावरण चर से इन विकल्पों को अग्रेषित करना ANALOG_OPTS
एक लॉन्च स्क्रिप्ट प्रदान करता है, जो कि ग्रेडल द्वारा उत्पन्न किया गया है।
export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
JavaMelody, , , . TCP- ; .
, , JVM AppCDS . JAVA_OPTS
JVM Unified Logging Framework :
export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa"
%p
, JVM (PID). AppCDS , ( ).
, . . :
server.port
nodes.this.agentPort
, :
export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
, ( ).
bin/analog
() http://localhost:8091 ,
PID ( ), :
pgrep -f analog 13792
pmap
( ):
pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB
; .
1-4 (, ).
pmap
. CDS' . , , PSS:
The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.
, , “ ” . , .
PSS , :
, - :
, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa
-Xshare:off
, CDS . , .

:
PSS AppCDS CDS.
. , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :
PSS AppCDS 2- ; 3- .
, , , . , AppCDS, , , 3- .
: , CDS? :
CDS/AppCDS JVM , PSS . , , pmap
, “” sed
'. :
pmap -X `pgrep -f analog` 14981:
( Mapping
) , “” . JVM ( libjvm.so
), ( libc-2.27.so
). :
For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so
) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.
. , , . , , JVM , Java- . GeekOut:

, , , AppCDS , .. Java-. , JVM, , - .
VisualVM Metaspace AppCDS , :
AppCDS

AppCDS

, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB
≈7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB
≈4,8 . , AppCDS , Metaspace. Metaspace, , CDS .
एक निष्कर्ष के बजाय
Spring Boot AppCDS – JVM, .
- JEP-350 Dynamic CDS Archives – JDK 13.
- Spring Boot ó CDS ( ). , 100% - 66% . , ≈11% ( 15%, ).
- , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
- Metaspace, , AppCDS 5 , CDS.
, , AppCDS, , “killer feature”. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , …
by Nick Fewings on Unsplash