.NET अनुप्रयोगों की मेमोरी उपयोग को कम करना?


108

.NET एप्लिकेशन की मेमोरी उपयोग को कम करने के लिए कुछ सुझाव क्या हैं? निम्नलिखित सरल सी # कार्यक्रम पर विचार करें।

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();
    }
}

विज़ुअल स्टूडियो के बाहर x64 के लिए रिलीज़ मोड में संकलित और कार्य प्रबंधक निम्नलिखित की रिपोर्ट करता है:

Working Set:          9364k
Private Working Set:  2500k
Commit Size:         17480k

यह थोड़ा बेहतर है अगर इसे सिर्फ x86 के लिए संकलित किया जाए :

Working Set:          5888k
Private Working Set:  1280k
Commit Size:          7012k

फिर मैंने निम्नलिखित कार्यक्रम की कोशिश की, जो कि समान है लेकिन रनटाइम इनिशियलाइज़ेशन के बाद प्रक्रिया के आकार को ट्रिम करने की कोशिश करता है:

class Program
{
    static void Main(string[] args)
    {
        minimizeMemory();
        Console.ReadLine();
    }

    private static void minimizeMemory()
    {
        GC.Collect(GC.MaxGeneration);
        GC.WaitForPendingFinalizers();
        SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
            (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
    }

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetProcessWorkingSetSize(IntPtr process,
        UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
}

Visual Studio के बाहर x86 रिलीज़ पर परिणाम :

Working Set:          2300k
Private Working Set:   964k
Commit Size:          8408k

जो थोड़ा बेहतर है, लेकिन यह अभी भी इस तरह के एक सरल कार्यक्रम के लिए अत्यधिक लगता है। क्या सी # प्रक्रिया को थोड़ा झुकाव देने के लिए कोई चाल है? मैं एक कार्यक्रम लिख रहा हूं जिसे ज्यादातर समय पृष्ठभूमि में चलाने के लिए डिज़ाइन किया गया है। मैं पहले से ही एक अलग अनुप्रयोग डोमेन में किसी भी उपयोगकर्ता इंटरफ़ेस सामान कर रहा हूँ जिसका अर्थ है कि उपयोगकर्ता इंटरफ़ेस सामान सुरक्षित रूप से अनलोड किया जा सकता है, लेकिन जब यह पृष्ठभूमि में बैठा है तो 10 एमबी लेना अत्यधिक लगता है।

पीएस के रूप में मैं क्यों परवाह करेगा --- (पावर) उपयोगकर्ता इन चीजों के बारे में चिंता करते हैं। भले ही इसका प्रदर्शन पर कोई प्रभाव न हो, लेकिन सेमी-टेक-सेवी उपयोगकर्ता (मेरे लक्षित दर्शक) पृष्ठभूमि एप्लिकेशन मेमोरी उपयोग के बारे में हसी फिट बैठता है। यहां तक ​​कि जब मैं 11 एमबी की मेमोरी लेता हुआ एडोब अपडेटर देखता हूं और फोबार 2000 के शांत स्पर्श से खुद को बेहोश महसूस करता हूं, जो खेलते समय भी 6 एमबी से कम का समय ले सकता है। मुझे पता है कि आधुनिक ऑपरेटिंग सिस्टम में, यह सामान वास्तव में तकनीकी रूप से बहुत मायने नहीं रखता है, लेकिन इसका मतलब यह नहीं है कि इसका धारणा पर कोई प्रभाव नहीं पड़ता है।


14
आप क्यों परवाह करेंगे? निजी कामकाजी सेट बहुत कम है। यदि स्मृति अनावश्यक है, तो आधुनिक OS डिस्क पर पेज करेगा। यह 2009 है। जब तक आप एम्बेडेड सिस्टम पर सामान नहीं बना रहे हैं, तब तक आपको 10 एमबी की परवाह नहीं करनी चाहिए।
मेहरदाद अफश्री

8
.NET का उपयोग करना बंद करें और आपके पास छोटे प्रोग्राम हो सकते हैं। .NET फ्रेमवर्क को लोड करने के लिए, कई बड़ी DLL को मेमोरी में लोड करने की आवश्यकता होती है।
एडम सिल 20

2
मेमोरी की कीमतें तेजी से गिरती हैं (हाँ, आप 24 जीबी रैम के साथ डेल होम कंप्यूटर सिस्टम का ऑर्डर कर सकते हैं)। जब तक आपका आवेदन> 500MB अनुकूलन का उपयोग कर रहा है अनावश्यक है।
एलेक्स

28
@LeakyCode मैं वास्तव में नफरत करता हूं कि आधुनिक प्रोग्रामर इस तरह सोचते हैं, आपको अपने एप्लिकेशन के मेमोरी उपयोग के बारे में ध्यान रखना चाहिए। मैं कह सकता हूं कि ज्यादातर आधुनिक अनुप्रयोग, जो जावा या सी # में लिखे गए हैं, संसाधन प्रबंधन की बात करते समय बहुत अप्रभावी होते हैं, और इसके लिए धन्यवाद कि 2014 में हम जितने आवेदन कर सकते हैं, उतने ही 1998 में हम win95 और 64mb पर वापस आ सकते हैं। ... ब्राउज़र का सिर्फ 1 उदाहरण अब 2GB RAM और सरल IDE खाता है 1gb के बारे में। राम सस्ते हैं, लेकिन इसका मतलब यह नहीं है कि आपको इसे बर्बाद करना चाहिए।
पेट्र

6
@Petr आपको संसाधन प्रबंधन की परवाह करनी चाहिए। प्रोग्रामर का समय एक संसाधन भी है। कृपया 10MB से 2GB तक का समय बर्बाद न करें।
मेहरदाद अफश्री

जवाबों:


33
  1. आप स्टैक ओवरफ्लो प्रश्न .NET EXE मेमोरी फुटप्रिंट की जांच कर सकते हैं ।
  2. MSDN ब्लॉग पोस्ट कार्य सेट! = वास्तविक मेमोरी फ़ुटप्रिंट सभी कार्य सेट, प्रक्रिया मेमोरी और आपके कुल इन-रैम खपत पर सटीक गणना करने के तरीके को ध्वस्त करने के बारे में है।

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

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

हालांकि, यहां कुछ लोगों को संबोधित करने के लिए, जो इसके बारे में चिंता नहीं करने के लिए कहते हैं: यदि आप एक विंडोज़ फॉर्म एप्लिकेशन लिख रहे हैं जो टर्मिनल सेवाओं के वातावरण में चल रहा होगा, तो संभवत: 10, 20 या अधिक उपयोगकर्ताओं द्वारा उपयोग किए गए साझा सर्वर पर, फिर हाँ , आप पूरी तरह से स्मृति उपयोग पर विचार करना चाहिए। और आपको सतर्क रहने की आवश्यकता होगी। इसे संबोधित करने का सबसे अच्छा तरीका अच्छा डेटा संरचना डिज़ाइन है और जब आप आवंटित करते हैं, तो सर्वोत्तम प्रथाओं का पालन करके।


45

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

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

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

  • वस्तुओं की संख्या कम करें और सुनिश्चित करें कि आवश्यकता से अधिक समय तक किसी भी उदाहरण पर पकड़ न रखें।
  • के बारे में पता है List<T>और इसी तरह की है कि डबल क्षमता जब जरूरत के रूप में वे 50% अप करने के लिए नेतृत्व कर सकते हैं।
  • आप स्टैक पर अधिक मेमोरी को मजबूर करने के लिए संदर्भ प्रकारों के मान प्रकारों का उपयोग करने पर विचार कर सकते हैं, लेकिन ध्यान रखें कि डिफ़ॉल्ट स्टैक स्पेस सिर्फ 1 एमबी है।
  • 85000 से अधिक बाइट्स की वस्तुओं से बचें, क्योंकि वे एलओएच पर जाएंगे जो कॉम्पैक्ट नहीं है और इस तरह आसानी से खंडित हो सकती है।

यह शायद किसी भी तरह से एक विस्तृत सूची नहीं है, लेकिन सिर्फ विचारों की एक जोड़ी है।


IOW, नेट में देशी कोड आकार के काम को कम करने के लिए उसी तरह की तकनीकें काम करती हैं?
राबर्ट फ्रेजर

मुझे लगता है कि कुछ ओवरलैप हैं, लेकिन मूल कोड के साथ आपके पास अधिक विकल्प हैं जब यह मेमोरी का उपयोग करने की बात आती है।
ब्रायन रासमुसेन

17

इस मामले में एक बात जिस पर आपको विचार करने की आवश्यकता है वह है सीएलआर की मेमोरी लागत। CLR को हर .Net प्रक्रिया के लिए लोड किया जाता है और इसलिए यह स्मृति में कारक होता है। इस तरह के एक सरल / छोटे कार्यक्रम के लिए सीएलआर की लागत आपके मेमोरी फुटप्रिंट पर हावी होने वाली है।

इस बेसलाइन प्रोग्राम की लागत की तुलना में वास्तविक एप्लिकेशन का निर्माण करना और उसकी लागत को देखने के लिए यह बहुत अधिक शिक्षाप्रद होगा।


7

प्रति विशिष्ट सुझाव नहीं, लेकिन आप CLR Profiler (Microsoft से मुफ्त डाउनलोड) पर एक नज़र डाल सकते हैं ।
इसे स्थापित करने के बाद, इस पृष्ठ पर एक नज़र डालें ।

कैसे करने के लिए:

यह कैसे दिखाता है कि आप अपने एप्लिकेशन की मेमोरी आवंटन प्रोफ़ाइल की जांच करने के लिए CLR Profiler टूल का उपयोग कैसे करते हैं। आप कोड की पहचान करने के लिए CLR Profiler का उपयोग कर सकते हैं जो मेमोरी समस्याओं का कारण बनता है, जैसे कि मेमोरी लीक और अत्यधिक या अक्षम कचरा संग्रह।


7

"वास्तविक" एप्लिकेशन के मेमोरी उपयोग को देखना चाहते हैं।

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


4

इस सरल कार्यक्रम के निजी कामकाजी सेट को कम करने के तरीके अभी भी हैं:

  1. अपना आवेदन एन.जी.एन. यह आपकी प्रक्रिया से जेआईटी संकलन लागत को हटा देता है।

  2. मेमोरी के उपयोग को कम करने और फिर इसे एनजीएन का उपयोग करके एमपीजीओ का उपयोग करके अपने आवेदन को प्रशिक्षित करें ।


2

आपके पदचिह्न को कम करने के कई तरीके हैं।

एक बात जो आपको हमेशा .NET में रहती है वह यह है कि आपके IL कोड की मूल छवि का आकार बहुत बड़ा है

और यह कोड पूरी तरह से एप्लिकेशन इंस्टेंस के बीच साझा नहीं किया जा सकता है। यहां तक ​​कि एनजीएनईड असेंबलियां पूरी तरह से स्थिर नहीं हैं, उनके पास अभी भी कुछ छोटे हिस्से हैं जिन्हें जेआईटी की आवश्यकता है।

लोग कोड लिखना भी पसंद करते हैं जो स्मृति को आवश्यकता से अधिक समय तक अवरुद्ध करता है।

एक अक्सर देखा गया उदाहरण: डेटाटैडर लेना, सामग्री को डेटाटेबल में लोड करना बस इसे XML फ़ाइल में लिखने के लिए। आप आसानी से OutOfMemoryException में चल सकते हैं। OTOH, आप एक XmlTextWriter का उपयोग कर सकते हैं और Datareader के माध्यम से स्क्रॉल कर सकते हैं, डेटाबेस के कर्सर के माध्यम से स्क्रॉल करते हुए XmlNodes का उत्सर्जन कर सकते हैं। इस तरह, आपके पास केवल वर्तमान डेटाबेस रिकॉर्ड और मेमोरी में XML आउटपुट है। जो कभी भी (या संभावना नहीं है) एक उच्च कचरा संग्रह पीढ़ी प्राप्त करेगा और इस प्रकार पुन: उपयोग किया जा सकता है।

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

C # में एक उत्कृष्ट विशेषता है जिसे पुनरावृत्तियों कहा जाता है। वे आपको अपने इनपुट के माध्यम से स्क्रॉल करके ऑब्जेक्ट को स्ट्रीम करने की अनुमति देते हैं और केवल वर्तमान उदाहरण को तब तक बनाए रखते हैं जब तक कि आप अगले एक को प्राप्त न कर लें। यहां तक ​​कि LINQ का उपयोग करके, आपको अभी भी इसके चारों ओर रखने की आवश्यकता नहीं है क्योंकि आप इसे फ़िल्टर करना चाहते थे।


1

शीर्षक में सामान्य प्रश्न को संबोधित करना और विशिष्ट प्रश्न नहीं:

यदि आप एक COM घटक का उपयोग कर रहे हैं जो बहुत सारा डेटा लौटाता है (बड़े 2xN सरणियों को दोगुना कहें) और केवल एक छोटे से अंश की आवश्यकता होती है, तो कोई एक आवरण COM घटक लिख सकता है जो मेमोरी को .NET से छुपाता है और केवल वह डेटा वापस लौटाता है जो है जरूरत है।

यही मैंने अपने मुख्य अनुप्रयोग में किया और इससे मेमोरी की खपत में काफी सुधार हुआ।


0

मैंने पाया है कि SetProcessWorkingSetSize या EmptyWorkingSet API का उपयोग करके मेमोरी पेजों को समय-समय पर लंबे समय तक चलने के लिए मजबूर करने के लिए मशीन पर सभी उपलब्ध भौतिक मेमोरी में परिणाम हो सकते हैं जब तक कि मशीन रिबूट नहीं हो जाती। हमारे पास नेट डीएलएल एक देशी प्रक्रिया में भरी हुई थी जो मेमोरी सघन कार्य करने के बाद काम करने वाले सेट को कम करने के लिए EmptyWorkingSet API (SetProcessWorkingSetSize का उपयोग करने का विकल्प) का उपयोग करेगी। मैंने पाया कि 1 दिन से लेकर सप्ताह के बीच कहीं भी एक मशीन टास्क मैनेजर में 99% भौतिक मेमोरी उपयोग दिखाती है, जबकि कोई भी प्रक्रिया किसी भी महत्वपूर्ण मेमोरी उपयोग का उपयोग नहीं करती है। जल्द ही मशीन अनुत्तरदायी हो जाएगी, जिसके लिए एक कठिन रिबूट की आवश्यकता होगी। कहा गया कि मशीनें 2 दर्जन से अधिक विंडोज सर्वर 2008 आर 2 और 2012 आर 2 सर्वर फिजिकल और वर्चुअल हार्डवेयर पर चल रही हैं।

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

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