2012-02-21 15 views
9

से पता लगाने योग्य नहीं है इसलिए मैं जीयूआई पर 2 विचार लोड करने के लिए एमवीवीएम + डाटा टेम्पलेट विधि के साथ WPF 3.5 का उपयोग कर रहा हूं। मैंने देखा है कि आइटम नियंत्रण के आइटम कंटेनर के हिस्से के रूप में जेनरेट की गई वस्तुओं को स्मृति में पिन किया गया है और दृश्य को अनलोड होने के बाद भी जीसीड नहीं मिला है!जीसी के लिए पिन किए गए उदाहरण - मेरे प्रबंधित कोड

मैंने अभी परीक्षण चलाया और पाया कि यह कोड के सबसे सरल के लिए भी पुन: उत्पन्न होता है ... आप लोग स्वयं की जांच कर सकते हैं।

XAML:

<Window x:Class="ContentControlVMTest.Window2" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:ContentControlVMTest" 
     Title="Window2" Height="300" Width="300"> 
    <DockPanel LastChildFill="True"> 

     <CheckBox Click="CheckBox_Click" Content="Test1?" 
        DockPanel.Dock="Top" Margin="5"/> 

     <ContentControl x:Name="contentControl"> 
      <ContentControl.Resources> 

       <DataTemplate DataType="{x:Type local:Test3}"> 
        <TextBlock Text="{Binding C}" Margin="5"/> 
       </DataTemplate> 

       <DataTemplate DataType="{x:Type local:Test1}"> 
        <DockPanel LastChildFill="True" Margin="5"> 
         <TextBlock Text="{Binding A}" 
            DockPanel.Dock="Top" 
            Margin="5"/> 
         <ListBox ItemsSource="{Binding Bs}" 
           DisplayMemberPath="B" 
           Margin="5"/> 
        </DockPanel> 
       </DataTemplate> 
      </ContentControl.Resources> 
     </ContentControl> 
    </DockPanel> 
</Window> 

कोड के पीछे:

public class Test3 
{ 
    public string C { get; set; } 
} 

public class Test2 
{ 
    public string B { get; set; } 
} 

public class Test1 
{ 
    public string A { get; set; } 

    private List<Test2> _Bs; 
    public List<Test2> Bs 
    { 
     get 
     { 
      return _Bs; 
     } 

     set 
     { 
      _Bs = value; 
     } 
    } 
} 

public partial class Window2 : Window 
{ 
    public Window2() 
    { 
     InitializeComponent(); 
     this.KeyDown += Window_KeyDown; 
    } 

    private void Window_KeyDown 
      (object sender, System.Windows.Input.KeyEventArgs e) 
    { 
     if (Keyboard.IsKeyDown(Key.LeftCtrl)) 
      if (Keyboard.IsKeyDown(Key.LeftShift)) 
       if (Keyboard.IsKeyDown(Key.LeftAlt)) 
        if (Keyboard.IsKeyDown(Key.G)) 
        { 
         GC.Collect(2, GCCollectionMode.Forced); 
         GC.WaitForPendingFinalizers(); 
         GC.Collect(2, GCCollectionMode.Forced); 
         GC.WaitForPendingFinalizers(); 
         GC.Collect(3, GCCollectionMode.Forced); 
         GC.WaitForPendingFinalizers(); 
         GC.Collect(3, GCCollectionMode.Forced); 
        } 
    } 

    private void CheckBox_Click(object sender, RoutedEventArgs e) 
    { 
     if (((CheckBox)sender).IsChecked.GetValueOrDefault(false)) 
     { 
      var x = new Test1() { A = "Test1 A" }; 
      x.Bs = new List<Test2>(); 
      for (int i = 1; i < 10000; i++) 
      { 
       x.Bs.Add(new Test2() { B = "Test1 B " + i }); 
      } 
      contentControl.Content = x; 
     } 
     else 
     { 
      contentControl.Content = new Test3() { C = "Test3 C" }; 
     } 
    } 
} 

मैं वाम शिफ्ट द्वारा जीसी मजबूर प्रदर्शन + Alt + Ctrl + जी Test1 या Test3 दृश्य के लिए सभी वस्तुओं और मॉडल को सही ढंग से उतारने के बाद मृत हो जाता है। तो यह उम्मीद है।

लेकिन संग्रह Test1 मॉडल (जिसमें Test2 ऑब्जेक्ट्स) में उत्पन्न संग्रह है, स्मृति में पिन किया गया है। और यह इंगित करता है कि सरणी सूची बॉक्स के आइटम कंटेनर द्वारा उपयोग की जाती है क्योंकि यह सूची बॉक्स से डी-आभासी वस्तुओं की संख्या दिखाती है! Test1 दृश्य मोड में दृश्य को कम या पुनर्स्थापित करते समय यह पिन किया गया सरणी इसका आकार बदलती है! एक बार यह 16 आइटम था और अगली बार जब प्रोफाइल किया गया तो यह 69 आइटम था।

enter image description here

इस WPF आइटम नियंत्रण में उत्पन्न वस्तुओं के pinning करता है इसका मतलब है! क्या कोई इसे समझा सकता है? क्या इसमें कोई संदेहजनक कमी है?

Thx बहुत कुछ।

+0

शायद एक 'CollectionView' संग्रह चारों ओर व्यतीत कर रहा है के लिए बनाया गया है। प्रतिक्रिया के लिए – user7116

+0

thx। हाँ! यह आइटम कंटेनर से आइटम संग्रह है। लेकिन वह क्यों लटका होगा? दृश्य चला गया है। Listbox चला गया है। यादगार में डब्ल्यूपीएफ पिन संग्रह क्यों होगा? –

+0

क्या आप पिन किए गए उदाहरण के लिए रूट पोस्ट कर सकते हैं। –

उत्तर

3

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

यह a bug report that I submitted over a year ago में वर्णित अनुसार अंतर्निहित समस्या से संबंधित प्रतीत होता है क्योंकि पिनिंग रूट और पिन किए गए उदाहरण पेड़ समान हैं। (एक सुविधाजनक प्रारूप में उस तरह के विवरण को देखने के लिए, हो सकता है कि आप ANTS Memory Profiler जैसे कुछ फैनसीयर मेमोरी प्रोफाइलर की प्रतिलिपि लेना चाहें।)

वास्तव में बुरी खबर यह है कि आपके अनाथ उदाहरणों की मृत्यु के पीछे पिन किया जा रहा है खिड़की स्वयं, तो आप संभवतः बाध्यकारी निजीकरण के लिए मजबूर करने के लिए WinForms परिदृश्य में उपयोग करने के लिए उसी तरह के हैक के बिना उन्हें साफ़ नहीं कर सकते हैं।

इन सब में अच्छी खबरों का एकमात्र बिट यह है कि समस्या तब नहीं होती है जब आप नेस्टेड गुणों के लिए बाध्यकारी से बच सकें। उदाहरण के लिए, यदि आप अपनी बी प्रॉपर्टी के मूल्य को वापस करने के लिए टेस्ट 2 पर एक ToString() ओवरराइड जोड़ते हैं और आप Listbox आइटम से DisplayMemberPath को हटाते हैं, तो समस्या दूर हो जाएगी। उदा .:

public class Test2 
{ 
    public string B { get; set; } 

    public override string ToString() 
    { 
     return this.B; 
    } 
} 

<ListBox ItemsSource="{Binding Bs}" 
    Margin="5"/> 
+1

आह हाँ कुख्यात बाध्यकारी रिसाव मुद्दा। यह भी सच है, मैंने इसे देखा है। यद्यपि आप इसे अपने मॉडल पर बदलकर INotifyProperty लागू करके इसे ठीक कर सकते हैं। यहां तक ​​कि यदि आप कभी भी PropertyChanged ईवेंट को कॉल नहीं करते हैं, तो इसकी उपस्थिति ढांचे में इस मुद्दे को हल करती है। बाध्यकारी में भी आप बाध्यकारी रिसाव मुद्दे को ठीक करने के लिए मोड = वनवे या मोड = वनटाइम जोड़ सकते हैं। हालांकि यह हमेशा उपलब्ध नहीं है। दोनों को आज़माएं, INotifyPropertyChanged और {बाध्यकारी बी, मोड = OneWay} जोड़ें। –

+0

@ निकोल और जस्टिन, आपकी मदद के लिए बहुत कुछ। ToString() या OneTime बाइंडिंग का उपयोग अनाथ टेस्ट 2 उदाहरणों को ठीक किया गया है। उसके लिए फिर से Thx। लेकिन सरणी टेस्ट 2 का एक उदाहरण [] अभी भी पिन किया गया है और स्मृति के 16 बाइट्स पर कब्जा कर लिया है। हालांकि यह स्मृति के लिए पिन किए गए टेस्ट 2 उदाहरणों की "n" संख्या होने से कहीं बेहतर है, लेकिन फिर भी इसे कैसे निकाला जाए? मैंने 'बीएस' संग्रह के लिए 'INotifyPropertyChanged' भी कोशिश की (भी 'बीएस' के थका हुआ वनटाइम बाध्यकारी) ... सरणी अभी भी रहती है। कोई मदद यहाँ? –

+0

@ justin.m.chase: INotifyPropertyChanged वर्कअराउंड का उल्लेख करने के लिए धन्यवाद।यह मेरी पिछली WinForms समस्याओं के लिए काम नहीं करता था, इसलिए मैं पूरी तरह से भूल गया था कि मैंने इसका उल्लेख WPF के लिए किया था। –

0

उपरोक्त नमूना कोड में मैं नहीं देखता कि आप किसी भी दृश्य को अनलोड कर रहे हैं?

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

तो आपको प्रेषक को पंप करने और फिर जीसी कॉल करने के लिए क्या करना है। कुछ इस तरह:

void Control_Unloaded(object sender, RoutedEventArgs e) 
{ 
    // flush dispatcher 
    this.Dispatcher.BeginInvoke(new Action(DoMemoryAnalysis), DispatcherPriority.ContextIdle); 
} 

private static void DoMemoryAnalysis() 
{ 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    // do memory analysis now. 
} 

एक और वास्तव में आम .net में मेमोरी लीक के कारण घटनाओं संलग्न और उन्हें सही ढंग से unattaching नहीं के साथ क्या करना है। मैं आपको उपरोक्त अपने नमूने में ऐसा नहीं देख रहा हूं, लेकिन यदि आप घटनाओं को जोड़ रहे हैं तो सुनिश्चित करें कि आप उन्हें अनलोड किए गए या निपटान में या जहां कहीं भी उपयुक्त हैं, को अनैच कर रहे हैं।

+0

एमवीवीएम + डाटा टेम्पलेट मॉडल में कोई दृश्य नहीं है, मॉडल या मॉडल ऑब्जेक्ट्स को मेमरी कोज़ में देखें, जब दृश्य एक अलग लक्ष्य दृश्य मॉडल पर ओवरराइट किया जाता है सेट है मेरा स्नैपशॉट दिखाता है कि 'टेस्ट 1' व्यू लाइव नहीं है! तो यह आपकी पहली क्वेरी का जवाब देता है ... प्लस मेरे पास कोई लीकिंग ईवेंट हैंडलर नहीं है क्योंकि ** पिन किए गए ** उदाहरणों के लिए रूट प्रोफाइलर द्वारा अवांछनीय (किसी भी ईवेंट हैंडलर के लिए) है। जैसा कि मेरे कोड में देखा गया है, मेरे पास वास्तव में कोई घटना नहीं है (जब तक डब्ल्यूपीएफ कुछ कारणों से संदर्भ को जीवित नहीं रखता)। प्लस ** पिनिंग ** उदाहरण हमेशा जानबूझकर और मेरे उदा। ऐसा कोई कोड नहीं है। –

+0

WPF स्वयं आपके दृश्य को सभी प्रकार की घटनाओं के लिए प्रेषक कतार में डाल देगा। ऐसा होने के लिए आपको अपने कोड में ईवेंट को हुक अप करने की आवश्यकता नहीं है। अभिव्यक्ति मिश्रण में PSD आयातक पर काम करते समय मुझे यह वही समस्या थी। हम एक बड़ी पीएसडी फाइल खोलेंगे, संवाद बंद करें और इसे फिर से खोलें और मेमोरी अपवाद से बाहर दुर्घटनाग्रस्त हो जाएंगे। यदि खिड़की भी खुली नहीं है तो आप स्मृति से कैसे बाहर हो सकते हैं? इसकी वजह यह है कि विंडो कुछ समय के लिए प्रेषक कतार में रहती है। दृश्य को अब भी दृश्य पेड़ में नहीं होने के बावजूद आपको कतार और जीसी एकत्रित करना होगा। कोशिश करो। –

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