2011-07-28 1 views
10

이 발생합니다. ResourceDictionaries는 항상 인스턴스화되므로 XAML에서 리소스를 찾을 때마다 메모리에서 한 번씩 리소스 사전을 여러 번 가져올 수 있습니다. 그래서 위에서 언급 한 해결책은 아주 좋은 대안처럼 보입니다. 사실 우리의 현재 프로젝트에서이 트릭은 많이 했어요 ... 메모리 소비가 800MB에서 44MB로 크게 커졌습니다. 불행하게도이 솔루션은 비용을 부담해야합니다. 여기에 표시하고 싶습니다. SharedResourceDictionary을 사용하는 동안이를 피할 수있는 방법을 찾으십시오.SharedResourceDictionary를 사용할 때 메모리 누수가 발생했습니다. 에 익숙한 일부 큰 wpf 응용 프로그램에서 작업 한 경우

공유 리소스 사전을 사용하여 문제를 시각화하는 작은 예제를 만들었습니다.

간단한 WPF 응용 프로그램을 만들면됩니다. 이제 UserControl을 추가 한 자원 XAML

Shared.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 

    <SolidColorBrush x:Key="myBrush" Color="Yellow"/> 

</ResourceDictionary> 

를 추가합니다. 숨김은 기본, 그래서 난 그냥

MyUserControl.xaml이

<UserControl x:Class="Leak.MyUserControl" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:SharedResourceDictionary="clr-namespace:Articy.SharedResourceDictionary" Height="128" Width="128"> 

    <UserControl.Resources> 
     <ResourceDictionary> 
      <ResourceDictionary.MergedDictionaries> 
       <ResourceDictionary Source="/Leak;component/Shared.xaml"/> 
      </ResourceDictionary.MergedDictionaries> 
     </ResourceDictionary> 
    </UserControl.Resources> 

    <Grid> 
     <Rectangle Fill="{StaticResource myBrush}"/>  
    </Grid> 
</UserControl> 

창 코드 뒤에이

Window1.xaml.cs 같은 것을 보이는 XAML을 보여

// [ ... ] 
    public Window1() 
    { 
     InitializeComponent(); 
     myTabs.ItemsSource = mItems; 
    } 

    private ObservableCollection<string> mItems = new ObservableCollection<string>(); 

    private void OnAdd(object aSender, RoutedEventArgs aE) 
    { 
     mItems.Add("Test"); 
    } 
    private void OnRemove(object aSender, RoutedEventArgs aE) 
    { 
     mItems.RemoveAt(mItems.Count - 1); 
    } 

그리고이 창 xaml은

입니다.

Window1.xaml

<Window.Resources> 
     <DataTemplate x:Key="myTemplate" DataType="{x:Type System:String}"> 
      <Leak:MyUserControl/> 
     </DataTemplate> 
    </Window.Resources> 

    <Grid> 
     <DockPanel> 
      <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 
       <Button Content="Add" Click="OnAdd"/> 
       <Button Content="Remove" Click="OnRemove"/> 
      </StackPanel> 
      <TabControl x:Name="myTabs" ContentTemplate="{StaticResource myTemplate}"> 
      </TabControl> 
     </DockPanel> 
    </Grid> 
</Window> 

나는 프로그램이 완벽하지 알고 propably 쉽게 할 수 있지만, 문제를 보여주는 방법을 파악하는 동안이 내가 생각 해낸 것입니다. 어쨌든 :

메모리 시작과 메모리 소비량을 확인하십시오. 메모리 프로파일 러가 있으면 훨씬 쉽게됩니다. 추가 (탭을 클릭하여 표시)하고 페이지를 제거하면 모든 것이 잘 작동하는 것을 볼 수 있습니다. 아무것도 누출되지 않습니다. 이제 UserControl.Resources 섹션에서 ResourceDictionary 대신 SharedResourceDictionary을 사용하여 Shared.xaml을 포함하십시오. 페이지를 제거한 후 MyUserControl이 메모리에 유지되고 그 안에 MyUserControl이 있음을 알 수 있습니다.

XAML을 통해 변환기, 사용자 컨트롤 등을 통해 인스턴스화 된 모든 작업에 이러한 문제가있는 것으로 나타났습니다. 이상하게도 사용자 지정 컨트롤에는 적용되지 않습니다. 사용자 지정 컨트롤, 데이터 템플릿 등은 실제로 인스턴스화되지 않으므로 내 추측입니다.

먼저 어떻게 피할 수 있습니까? 우리의 경우에는 SharedResourceDictionary를 사용하는 것이 필수적이지만 메모리 누수로 인해 생산적으로 사용할 수 없게됩니다. UserControls 대신 CustomControls를 사용하여 누출을 피할 수 있습니다. 이는 항상 실제적인 것은 아닙니다. 그렇다면 UserControls는 ResourceDictionary에서 강력하게 참조하는 이유는 무엇입니까? 이전에 경험 한 경험이없는 이유가 궁금합니다. 이전 질문에서 말했듯이 리소스 사전과 XAML을 절대적으로 잘못 사용하는 것처럼 보입니다. 그렇지 않으면 왜 비효율적인지 궁금합니다.

누군가가이 문제에 대해 밝힐 수 있기를 바랍니다. 사전 니코

+0

의을 MyUserControl 인스턴스에 대한 참조를 유지하는 것을 목적이었다? –

+0

확실히 기억할 수는 없지만, ResourceDictionary라고 생각됩니다. 내 경우에는 SharedResourceDictionary입니다. – dowhilefor

답변

5

에서

덕분에 나는이 문제를 해결할 수 있는지를 확실히 모르겠습니다. 그러나 나는 컨트롤을 참조하는 ResourceDictionary과 비슷한 문제를 가지고 있으며, 게으른 hydration과 관련이 있습니다. 여기에 post이 있습니다. 나는 큰 틱 WPF 프로젝트에서 공유 자원 디렉토리를 필요로 같은 문제로 실행 해요

public partial class App : Application 
{ 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     WalkDictionary(this.Resources); 

     base.OnStartup(e); 
    } 

    private static void WalkDictionary(ResourceDictionary resources) 
    { 
     foreach (DictionaryEntry entry in resources) 
     { 
     } 

     foreach (ResourceDictionary rd in resources.MergedDictionaries) 
      WalkDictionary(rd); 
    } 
} 
+0

링크를 이용해 주셔서 감사합니다. 이미 도움이되었습니다. 우리가 모든 리소스를 조용히 가지고 있기 때문에 모든 리소스를 순회하는 실제 솔루션은 정확하게 옵션이 아닙니다. 더 나은 것이 없으면 당신의 대답을 최선의 답으로 표시 할 것입니다. – dowhilefor

9

: 그리고이 코드는 내 문제를 해결. 원본 기사와 주석을 읽은 후 주석에 제안 된대로 SharedDirectory 클래스에 몇 가지 수정 사항을 포함 시켰습니다.이 참조는 _sourceUri에 저장된 강력한 참조를 제거하고 디자이너가 올바르게 작동하도록합니다. 나는 당신의 예제를 테스트했고, 디자이너와 MemProfiler 모두 성공적으로 언급 된 것을 언급하지 않았다. 나는 사람이 더 그것을 개선 알고 싶어요,하지만 난 지금은 함께 갈거야 무엇 : 메모리 프로파일 러에서

public class SharedResourceDictionary : ResourceDictionary 
{ 
    /// <summary> 
    /// Internal cache of loaded dictionaries 
    /// </summary> 
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries = 
     new Dictionary<Uri, ResourceDictionary>(); 

    /// <summary> 
    /// Local member of the source uri 
    /// </summary> 
    private Uri _sourceUri; 

    /// <summary> 
    /// Gets or sets the uniform resource identifier (URI) to load resources from. 
    /// </summary> 
    public new Uri Source 
    { 
     get { 
      if (IsInDesignMode) 
       return base.Source; 
      return _sourceUri; 
     } 
     set 
     { 
      if (IsInDesignMode) 
      { 
       try 
       { 
        _sourceUri = new Uri(value.OriginalString); 
       } 
       catch 
       { 
        // do nothing? 
       } 

       return; 
      } 

      try 
      { 
       _sourceUri = new Uri(value.OriginalString); 
      } 
      catch 
      { 
       // do nothing? 
      } 

      if (!_sharedDictionaries.ContainsKey(value)) 
      { 
       // If the dictionary is not yet loaded, load it by setting 
       // the source of the base class 

       base.Source = value; 

       // add it to the cache 
       _sharedDictionaries.Add(value, this); 
      } 
      else 
      { 
       // If the dictionary is already loaded, get it from the cache 
       MergedDictionaries.Add(_sharedDictionaries[value]); 
      } 
     } 
    } 

    private static bool IsInDesignMode 
    { 
     get 
     { 
      return (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, 
      typeof(DependencyObject)).Metadata.DefaultValue; 
     } 
    } 
} 
+3

이 접근법에 대한 흥미로운 확장은 WeakReference를 사용하여 사용하지 않을 때 ResourceDictionary 객체를 가비지 수집 할 수 있습니다. 자세한 내용은 [이 블로그 게시물] (https://codeblitz.wordpress.com/2010/08/25/resourcedictionary-use-with-care/)을 참조하십시오. –

+0

Drew, 혹시 가비지 수집이 작동하는지 테스트 해 보셨습니까? – tofutim