2008-10-16 16 views
22

काम पर हमारे लक्षित प्लेटफार्मों में से एक संसाधन संसाधन अवरुद्ध मिनी-सर्वर चल रहा है लिनक्स (कर्नेल 2.6.13, पुराने फेडोरा कोर पर आधारित कस्टम वितरण)। एप्लिकेशन जावा में लिखा गया है (सूर्य जेडीके 1.6_04)। लिनक्स ओओएम हत्यारा प्रक्रियाओं को मारने के लिए कॉन्फ़िगर किया गया है जब स्मृति उपयोग 160 एमबी से अधिक है। यहां तक ​​कि उच्च भार के दौरान भी हमारा आवेदन 120 एमबी से अधिक नहीं होता है और कुछ अन्य मूल प्रक्रियाओं के साथ सक्रिय होता है जो हम ओओएम सीमा के भीतर अच्छी तरह से रहते हैं।क्या स्मृति कर्नेल/libc संस्करण जावा Runtime.exec() से स्मृति के संबंध में सुरक्षित है?

हालांकि, यह पता चला है कि Java Runtime.getRuntime()। Exec() विधि, जावा से बाहरी प्रक्रियाओं को निष्पादित करने के लिए कैननिकल तरीका, particularly unfortunate implementation on Linux है जिसके कारण बच्चे की प्रक्रियाओं को अस्थायी रूप से (अस्थायी रूप से) की आवश्यकता होती है पता स्थान की प्रतिलिपि बनाने के बाद माता-पिता की मूल प्रक्रिया के रूप में स्मृति। शुद्ध परिणाम यह है कि जैसे ही हम Runtime.getRuntime() निष्पादित करते हैं, ओओएम हत्यारे द्वारा हमारे आवेदन को मार दिया जाता है।

हम वर्तमान में एक अलग देशी कार्यक्रम करके सभी बाहरी कमांड निष्पादन करते हुए इसके आसपास काम करते हैं और हम उस कार्यक्रम के साथ सॉकेट पर संवाद करते हैं। यह इष्टतम से कम है।

posting about this problem online के बाद मुझे कुछ प्रतिक्रिया मिली है कि यह लिनक्स के "नए" संस्करणों पर नहीं होना चाहिए क्योंकि वे कॉपी-ऑन-राइट का उपयोग करके पॉज़िक्स फोर्क() विधि को लागू करते हैं, संभवतः इसका अर्थ यह है कि यह केवल उन पृष्ठों की प्रतिलिपि बनाएगा जिन्हें इसकी आवश्यकता है संशोधित करें जब तुरंत पूरे पता स्थान की आवश्यकता होती है।

मेरे प्रश्न हैं:

  • यह सच है?
  • क्या यह कर्नेल, libc कार्यान्वयन या कहीं और पूरी तरह से कुछ है?
  • फोर्क (lib) के कर्नेल/libc/जो भी कॉपी-ऑन-राइट उपलब्ध है, से कौन सा संस्करण उपलब्ध है?
+0

यह * आभासी *, नहीं * भौतिक * स्मृति है जो कि कांटा() कॉल और निम्न निष्पादन() के बीच आवश्यक है। मुझे बहुत संदेह है कि आप अपनी भौतिक स्मृति सीमा की तुलना में पता स्थान के आकार को देखते हुए वर्चुअल मेमोरी से बाहर हो रहे हैं। –

+0

बिल्कुल, हम भौतिक स्मृति से बाहर नहीं जा रहे हैं, लेकिन लिनक्स के कुछ हिस्से को लगता है कि हम हैं। –

उत्तर

10

यह बहुत समय है * निक्स (और लिनक्स) ने समय की शुरुआत के बाद से काम किया है (या mmus की सुबह को निष्क्रिय करें)।

* निक्सेस पर एक नई प्रक्रिया बनाने के लिए जिसे आप फोर्क() कहते हैं। कांटा() कॉलिंग प्रक्रिया की एक प्रति अपनी सभी मेमोरी मैपिंग्स, फाइल डिस्क्रिप्टर इत्यादि के साथ बनाता है। मेमोरी मैपिंग्स कॉपी-ऑन-राइट किया जाता है ताकि (इष्टतम मामलों में) कोई मेमोरी वास्तव में कॉपी नहीं की जाती है, केवल मैपिंग्स। एक निम्न निष्पादन() कॉल नए निष्पादन योग्य के साथ वर्तमान मेमोरी मैपिंग को प्रतिस्थापित करता है। तो, कांटा()/exec() एक नई प्रक्रिया बनाने का तरीका है और यही वह है जो JVM का उपयोग करता है।

चेतावनी व्यस्त प्रणाली पर बड़ी प्रक्रियाओं के साथ है, तो माता-पिता बच्चे के निष्पादन से पहले थोड़ी देर के लिए दौड़ना जारी रख सकते हैं() की प्रतिलिपि बनाने की वजह से बड़ी मात्रा में स्मृति की प्रतिलिपि बनाई जा सकती है । वीएम में, कचरा कलेक्टर की सुविधा के लिए स्मृति को बहुत अधिक स्थानांतरित किया जा सकता है जो और भी प्रतिलिपि बनाता है।

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

+1

मैं इस लाइटवेट प्रक्रिया एसएसएच को कॉल करूंगा और स्थानीयहोस्ट से कनेक्ट करने के लिए http://www.jcraft.com/jsch/ का उपयोग करूंगा। – JoG

1

1: हां। 2: यह दो चरणों में बांटा गया है: किसी भी सिस्टम कॉल जैसे कांटा() को ग्लिब द्वारा कर्नेल में लपेटा जाता है। सिस्टम-कॉल का कर्नेल हिस्सा कर्नेल/fork.c 3 में है: मुझे नहीं पता। लेकिन मैं शर्त लगाता हूं कि आपके कर्नेल में यह है।

ओओएम हत्यारा 32 बिट बक्से पर कम स्मृति की धमकी दी जाती है। मुझे इसके साथ कभी कोई समस्या नहीं हुई है, लेकिन ओओएम को बे में रखने के तरीके हैं। यह समस्या कुछ ओओएम कॉन्फ़िगरेशन समस्या हो सकती है।

चूंकि आप जावा एप्लिकेशन का उपयोग कर रहे हैं, तो आपको 64 बिट लिनक्स पर जाने पर विचार करना चाहिए। यह निश्चित रूप से इसे ठीक करना चाहिए। अधिकांश 32 बिट ऐप्स 64 बिट कर्नेल पर चल सकते हैं जब तक कि प्रासंगिक पुस्तकालय स्थापित नहीं होते हैं।

आप 32 बिट फेडोरा के लिए पीएई कर्नेल को भी आजमा सकते हैं।

+0

64 बिट JVM पर क्यों चल रहा है इसे ठीक करें? –

+1

@AlastairMcCormack 32 बिट लिनक्स सिस्टम ने उपयोगकर्ता स्पेस और कर्नेल के लिए 3 जीबी/1 जीबी विभाजन का इस्तेमाल किया। 1 जीबी का हिस्सा उपयोगकर्ता अंतरिक्ष प्रक्रियाओं के लिए आरक्षित है जो सिस्टम कॉल के माध्यम से कर्नेल स्पेस एक्सेस की आवश्यकता है। यह केवल 128 एम है। मेरा मानना ​​है कि 128 एम कर्नेल और 3 जीबी उपयोगकर्ता स्पेस में मेमोरी प्रेशर ओओएम को ट्रिगर करेगा। जबकि 3 जीबी स्पेस में अन्य प्रक्रियाएं हो सकती हैं, एक जेवीएम सबसे बड़ी प्रक्रिया बनने की संभावना है, और इसलिए ओओएम लक्ष्य है।ओपीएस जेवीएम की तरह एक फोर्किंग प्रक्रिया में ओओएम स्कोर अधिक है क्योंकि प्रत्येक बच्चा माता-पिता के स्कोर में जोड़ता है। 64 बिट में कोई मेमोरी स्प्लिट नहीं है या कर्नेल/उपयोगकर्ता विभाजन में से कोई भी नहीं है। – Bash

5

खैर, मुझे व्यक्तिगत रूप से संदेह है कि यह सच है, क्योंकि लिनक्स का कांटा() प्रतिलिपि के माध्यम से किया जाता है क्योंकि भगवान जानता है कि (कम से कम 2.2.x कर्नेल इसे था, और यह 199x में कहीं था) ।

चूंकि ओओएम हत्यारा एक कच्चा वाद्य यंत्र माना जाता है जो मिस्फीयर के लिए जाना जाता है (fe, यह आवश्यक रूप से उस प्रक्रिया को मारता है जो वास्तव में अधिकांश स्मृति आवंटित करता है) और जिसका उपयोग केवल अंतिम पुनर्वितरण के रूप में किया जाना चाहिए, मुझे स्पष्ट नहीं है कि आपने इसे 160 एम पर आग लगाने के लिए क्यों कॉन्फ़िगर किया है।

यदि आप स्मृति आवंटन पर एक सीमा लागू करना चाहते हैं, तो उलमिट आपका मित्र है, ओओएम नहीं।

मेरी सलाह है कि ओओएम अकेले छोड़ दें (या इसे पूरी तरह से अक्षम करें), ulimits को कॉन्फ़िगर करें, और इस समस्या को भूल जाओ।

+0

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

2

हां, यह बिल्कुल लिनक्स के नए संस्करणों के साथ मामला है (हम 64-बिट Red Hat 5.2 पर हैं)। मुझे लगभग 18 महीनों तक धीमी गति से चलने वाले सबप्रोसेसेस के साथ समस्या हो रही है, और जब तक मैं आपका प्रश्न नहीं पढ़ता तब तक समस्या को कभी भी समझ नहीं पाया और इसे सत्यापित करने के लिए एक परीक्षण चलाया।

हमारे पास 16 कोर के साथ 32 जीबी बॉक्स है, और यदि हम JVM को -Xms4g और -Xmx8g जैसे सेटिंग्स के साथ चलाते हैं और 16 थ्रेड के साथ Runtime.exec() का उपयोग करके उपप्रोसेसेस चलाते हैं, तो हम अपनी प्रक्रिया को चलाने में सक्षम नहीं हैं प्रति सेकंड लगभग 20 प्रक्रिया कॉल से तेज़।

लिनक्स में 10,000 बार के बारे में सरल "दिनांक" कमांड के साथ इसे आज़माएं। यदि आप क्या हो रहा है यह देखने के लिए प्रोफाइलिंग कोड जोड़ते हैं, तो यह जल्दी से शुरू होता है लेकिन समय के साथ धीमा हो जाता है।

अपना प्रश्न पढ़ने के बाद, मैंने अपनी मेमोरी सेटिंग्स को -Xms128m और -Xmx128m पर कम करने का प्रयास करने का निर्णय लिया। अब हमारी प्रक्रिया प्रति सेकंड लगभग 80 प्रक्रिया कॉल पर चलती है। जेवीएम मेमोरी सेटिंग्स मैं सब बदल गया था।

ऐसा लगता है कि मैंने कभी भी मेमोरी को चूसने की प्रतीत नहीं की है, जब भी मैंने 32 धागे के साथ कोशिश की थी। यह सिर्फ अतिरिक्त मेमोरी को किसी भी तरह से आवंटित किया जाना है, जिसके कारण भारी स्टार्टअप (और शायद शटडाउन) लागत हो सकती है।

वैसे भी, ऐसा लगता है कि इस व्यवहार को अक्षम करने के लिए एक सेटिंग होना चाहिए या शायद JVM में भी।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^