2012-01-24 11 views
7

मैं मुद्दों में भाग रहा हूं जब बड़ी मात्रा में स्मृति आवंटित करने पर नियंत्रण नष्ट हो जाता है और यूआई से इनपुट घटनाओं का उपयोग करके पुनर्निर्मित किया जाता है।एक निश्चित रूप से भारी WPF नियंत्रणों द्वारा उपयोग की जाने वाली एक निःशुल्क मेमोरी कैसे हो सकती है?

जाहिर है, खिड़की की सामग्री को शून्य पर सेट करना, नियंत्रण में उपयोग की गई स्मृति को मुक्त करने के लिए पर्याप्त नहीं है। अंत में यह जीसीड होगा। लेकिन इनपुट घटनाओं को नष्ट करने और निर्माण करने के लिए नियंत्रण अधिक बार मिलता है, जीसी कुछ वस्तुओं को छोड़ने लगता है।

मैं इसे इस उदाहरण के लिए खराब हो गई:

XAML:

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" x:Name="window" MouseMove="window_MouseMove"> 
</Window> 

सी #:

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     private class HeavyObject : UserControl 
     { 
      private byte[] x = new byte[750000000]; 

      public HeavyObject() 
      { 
       for (int i = 0; i++ < 99999999;) { } // just so we can follow visually in Process Explorer 
       Content = "Peekaboo!"; // change Content to el cheapo re-trigger MouseMove 
      } 
     } 

     public MainWindow() 
     { 
      InitializeComponent(); 

      //Works 1: 
      //while (true) 
      //{ 
      // window.Content = null; 
      // window.Content = new HeavyObject(); 
      //} 
     } 

     private void window_MouseMove(object sender, MouseEventArgs e) 
     { 
      if (window.Content == null) 
      { 
       GC.Collect(); 
       GC.WaitForPendingFinalizers(); 
       // Works 2: 
       //new HeavyObject(); 
       //window.Content = "Peekaboo!"; // change Content to el cheapo re-trigger MouseMove 
       //return; 
       window.Content = new HeavyObject(); 
      } 
      else 
      { 
       window.Content = null; 
      } 
     } 
    } 
} 

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

प्रोसेसएक्सप्लोरर/टास्कैन में हम क्या देख सकते हैं, यह कभी-कभी 1.5 जीबी या अधिक आवंटित होता है। यदि नहीं, तो माउस को जंगली ढंग से ले जाएं और विंडो छोड़ दें और फिर से दर्ज करें। यदि आप LARGEADDRESSAWARE के बिना x86 के लिए संकलित करते हैं, तो आपको इसके बजाय आउटऑफमेमरीएक्सप्शन मिलेगा (सीमा ~ 1.3 जीबी)।

यदि आप या तो माउस इनपुट (असम्बद्ध अनुभाग "वर्क्स 1") के बिना एक अंतहीन पाश में हेवी ऑब्जेक्ट आवंटित करते हैं तो आप इस व्यवहार का सामना नहीं करेंगे या यदि आप माउस इनपुट द्वारा आवंटन को ट्रिगर करते हैं लेकिन ऑब्जेक्ट को ऑब्जेक्ट में नहीं डालते दृश्य पेड़ (असुविधा seciton "वर्क्स 2")।

तो मुझे लगता है कि दृश्य वृक्ष आलसी मुक्त संसाधनों के साथ इसका कुछ संबंध है। लेकिन फिर एक और अजीब प्रभाव है: यदि कर्सर के दौरान 1.5 जीबी का उपभोग होता है तो खिड़की के बाहर होता है, ऐसा लगता है कि जीसी तब तक नहीं टिकेगा जब तक कि माउसमोव फिर से ट्रिगर नहीं हो जाता। कम से कम, स्मृति खपत तब तक स्थिर हो जाती है जब तक कोई और घटनाएं ट्रिगर नहीं होतीं। तो या तो वहां कोई लंबा जीवित संदर्भ है या कोई गतिविधि नहीं होने पर जीसी आलसी हो जाती है।

मेरे लिए पर्याप्त अजीब लगता है। क्या आप समझ सकते हैं कि क्या हो रहा है?

संपादित करें: रूप BalamBalam टिप्पणी की: नियंत्रण के पीछे डेटा नियंत्रण नष्ट करने से पहले dereferenced जा सकता है। यह योजना बी थी। फिर भी शायद एक और सामान्य समाधान है।

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

+2

मैं यह नहीं समझ सकता कि आपकी समस्या क्या है ... मान लीजिए कि आपके पास पागल कोड सिर्फ परीक्षण के लिए है। लेकिन आपने एक बात सीखी है: जीसी को मजबूर करने के प्रयास अक्सर ऐसा नहीं करते जो आपको लगता है कि वे करेंगे। –

+0

कोड एक उदाहरण है, जिसमें दो वर्कअराउंड होते हैं जहां समस्या नहीं होगी। अनुमानों को सीमित करने के लिए। समस्या यह है: विंडो सामग्री के रूप में बड़ा नियंत्रण रखें -> माउस इनपुट ** द्वारा विंडो सामग्री के रूप में नया बड़ा नियंत्रण सेट करें ** -> एक से अधिक बड़े नियंत्रण एक बार में जीवित हैं। –

+0

खैर, मुझे उम्मीद है कि कोई भी माउस चाल पर 750 एमबी ऑब्जेक्ट आवंटित नहीं करेगा। यह एक बहुत ही अवास्तविक परिदृश्य है, और इसलिए आपकी पूरी नींव यहां shakey है। –

उत्तर

7

ठीक है, मुझे लगता है कि मुझे पता चल सकता है कि यहां क्या हो रहा है। हालांकि इसे नमक की चुटकी से लें ...

मैंने आपके कोड को थोड़ा सा संशोधित किया।

Xaml

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" x:Name="window" MouseDown="window_MouseDown"> 
</Window> 

कोड के पीछे

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     private class HeavyObject : UserControl 
     { 
      private byte[] x = new byte[750000000]; 

      public HeavyObject() 
      { 
       for (int i = 0; i < 750000000; i++) 
       {      
        unchecked 
        { 
         x[i] = (byte)i;  
        } 
       } 

       // Release optimisation will not compile the array 
       // unless you initialize and use it. 
       Content = "Loadss a memory!! " + x[1] + x[1000]; 
      } 
     } 

     const string msg = " ... nulled the HeavyObject ..."; 

     public MainWindow() 
     { 
      InitializeComponent(); 
      window.Content = msg; 
     } 

     private void window_MouseDown(object sender, MouseEventArgs e) 
     { 
      if (window.Content.Equals(msg)) 
      { 
       window.Content = new HeavyObject(); 
      } 
      else 
      { 
       window.Content = msg; 
      } 
     } 
    } 
} 

अब इस ऊपर चलाने के लिए और खिड़की पर क्लिक करें। मैंने कोड को पर जीसी.कोलेक्ट() और का उपयोग नहीं किया है, mousedown पर मेमोरी सेट करें। मैं रिलीज मोड में संकलित कर रहा हूं और डीबगर संलग्न किए बिना चला रहा हूं।

विंडो पर धीरे-धीरे क्लिक करें।

पहला क्लिक आपको स्मृति उपयोग 750MBytes तक बढ़ता है। बाद के क्लिक .. Nulled the heavy object.. और Loads a memory!के बीच संदेश को तब तक टॉगल करते हैं जब तक आप धीरे-धीरे क्लिक करते हैं। मेमोरी आवंटित और अस्वीकृत होने के साथ थोड़ा सा अंतराल है। मेमोरी उपयोग 750 एमबीएट्स से अधिक नहीं होना चाहिए, लेकिन जब हेवीबोजेक्ट को हटा दिया जाता है तो यह भी कम नहीं होता है। यह GC Large Object Heap is lazy and collects large object memory when new large object memory is needed के रूप में डिज़ाइन किया गया है। एमएसडीएन से:

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

क्लिक करें

20 क्लिक के चारों ओर। क्या होता है? मेरे पीसी मेमोरी उपयोग पर नहीं बढ़ता है लेकिन मुख्यविंडो अब संदेश को अपडेट नहीं करता है। यह जमा देता है। मुझे स्मृति अपवाद से बाहर नहीं मिला है और मेरे सिस्टम का समग्र स्मृति उपयोग फ्लैट रहता है।

तनाव प्रणाली खिड़की पर MouseMove का उपयोग कर

अब (अपने प्रश्न कोड के अनुसार) इस Xaml बदलकर एक MouseMove घटना के लिए MouseDown बदल देते हैं:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" x:Name="window" MouseMove="window_MouseDown"> 
</Window> 

मैं माउस स्थानांतरित कर सकते हैं इसके लिए थोड़ी देर के लिए लेकिन अंत में यह एक आउटफमेमरी अपवाद फेंकता है। अपवाद को मजबूर करने का एक अच्छा तरीका कई चाल है तो विंडो को अधिकतम करने का प्रयास करें।

थ्योरी

ठीक है तो यह मेरा सिद्धांत है। चूंकि आपका यूआई थ्रेड एक संदेश लूप इवेंट (माउसमोव) के अंदर मेमोरी बनाने और हटाने के लिए फ्लैट चला रहा है, कचरा कलेक्टर जारी रखने में असमर्थ है। बड़े ऑब्जेक्ट हीप पर कचरा संग्रह when memory is needed किया जाता है। यह भी एक महंगा काम है, जितना संभव हो उतना बार किया जाता है।

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

सिस्टम कम मेमोरी स्थिति में है: ऐसा तब होता है जब मुझे ओएस से उच्च स्मृति अधिसूचना मिलती है। अगर मुझे लगता है कि पीढ़ी 2 जीसी करना उत्पादक होगा, तो मैं एक ट्रिगर करूंगा।

अब इस हिस्से दिलचस्प

अंत में, अभी के रूप में, LOH संग्रह का एक भाग के रूप में जमा नहीं किया जाता है और मैं यहाँ अनुमान लगा रहा हूँ, लेकिन, लेकिन वह एक कार्यान्वयन विस्तार है कि ऐसा करना चाहिए है पर भरोसा नहीं किया जाएगा। इसलिए यह सुनिश्चित करने के लिए कि जीसी द्वारा कुछ स्थानांतरित नहीं किया गया है, हमेशा इसे पिन करें। अब अपना नया LOH ज्ञान लें और ढेर पर नियंत्रण रखें।

तो, अगर बड़े वस्तु ढेर जमा नहीं है, और आप एक तंग पाश में कई बड़ी वस्तुओं का अनुरोध, यह संभव है कि संग्रह अंततः एक OutOfMemoryException भले ही सैद्धांतिक रूप से पर्याप्त स्मृति होना चाहिए के कारण विखंडन के लिए नेतृत्व?

समाधान

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

private void window_MouseDown(object sender, MouseEventArgs e) 
    { 
     Dispatcher.BeginInvoke((ThreadStart)delegate() 
      { 
       if (window.Content.Equals(msg)) 
       { 
        window.Content = new HeavyObject(); 
       } 
       else 
       { 
        window.Content = msg; 
       } 
      }, DispatcherPriority.ApplicationIdle); 
    } 

इस का उपयोग करते हुए मैं सभी प्रपत्र पर माउस नृत्य कर सकते हैं और यह कभी एक OutOfMemoryException फेंकता है। चाहे वह वास्तव में काम कर रहा हो या जिस समस्या को मैं जानता हूं उसे "निश्चित" किया गया है लेकिन इसका परीक्षण योग्य है।

अंत में

  • ध्यान दें कि यह आकार की वस्तुओं ढेर
  • LOH आवंटन एक जीसी को गति प्रदान करता है, तो पर्याप्त नहीं स्थान उपलब्ध
  • LOH है बड़े वस्तु पर आवंटित किया जा रहे हैं एक जीसी चक्र के दौरान संकुचित नहीं किया गया है, इसलिए विखंडन हो सकता है, सैद्धांतिक रूप से पर्याप्त स्मृति होने के बावजूद आउटऑफमेमरी अपवाद
  • आदर्श रूप से बड़ी वस्तुओं का पुन: उपयोग करें और पुन: आवंटित न करें। एक पृष्ठभूमि धागा में अपनी बड़ी वस्तुओं यूआई धागे से दसगुणा
    • Allcate
    • जीसी कमरे साँस लेने के लिए अनुमति देने के लिए, आवंटन दसगुणा तक आवेदन निष्क्रिय है डिस्पैचर का उपयोग
    : अगर यह संभव नहीं है
+0

इस विस्तार के लिए धन्यवाद। मैंने आपके परीक्षणों का पुनरुत्पादन किया। "20 क्लिक" = फ्रीज मेरी मशीन पर नहीं हुआ था। खिड़की अपरिवर्तित बनी रही जब तक कि सभी क्लिकों को संभाला नहीं गया (थोड़ी देर लग गई) और फिर सामान्य रूप से अपडेट करने के लिए लौट आया। शायद आप काफी देर तक इंतजार नहीं कर रहे थे? स्मृति व्यवहार वही है। तनाव के नीचे खिड़की को अधिकतम करने के लिए माउस कर्सर (मूल प्रश्न के रूप में) के साथ खिड़की को छोड़कर और फिर से प्रवेश करने जैसा ही लगता है। हो सकता है कि छुट्टी और प्रवेश घटनाएं स्मृति आवंटित करें ताकि अगली हेवी ऑब्जेक्ट अब पुराने के अंतराल में फिट न हो? –

+1

@die_hoernse, यह धीमी गति रखने के लिए सॉफ्टवेयर के लिए मजबूर करने आप DoBigMemoryOperation समारोह से घटना क्लिक करें दसगुणा और जब तक BigMemoryOperation वापस आ गया है फिर से entrancy को रोकने के लिए कम प्राथमिकता के साथ डिस्पैचर इस्तेमाल कर सकते हैं। एक पृष्ठभूमि सूत्र में अपने BigMemoryOperation कर भी बटन काम करते हुए अक्षम करने प्रगति की रिपोर्टिंग की अनुमति देता है और यूआई बाहर graying/- इस तरह चालें वास्तव में जब जीसी –

+0

पर जोर दिया "तनाव" मोड में, उत्पादन दुर्घटनाओं को रोकने में मदद कर सकता है जब वहाँ दो बार (या अधिक - HeavyObject की स्मृति आवंटित और कर्सर खिड़की के बाहर है, यह कर्सर फिर से प्रवेश करती है जब तक आवंटित रहता बना यह 750MB के बजाय 250)। मेरा सिद्धांत है, कार्यक्रम की निष्क्रियता और सिस्टम मेमोरी दबाव के कारण जीसी निष्क्रिय है। यह देखते हुए कि कोई रिसाव नहीं है लेकिन LOH अंततः संकलित किया जाएगा, मैं आपके अंतिम निष्कर्ष समाधान पर विचार करता हूं: इसे पृष्ठभूमि धागे में रखने के लिए। हालांकि, अगर मुझे पता चलेगा कि स्मृति हमेशा के लिए आवंटित रहता है, यहां तक ​​कि उल्लेखनीय I/O या CPU लोड के बिना स्मृति दबाव के तहत भी, यह प्रश्न अपडेट किया जाएगा। –

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

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