2011-07-28 22 views
10

nếu bạn làm việc trên một số ứng dụng wpf lớn hơn, bạn có thể quen thuộc với this. Bởi vì ResourceDictionaries luôn được khởi tạo, mỗi khi chúng được tìm thấy trong XAML, chúng ta có thể sẽ có một từ điển tài nguyên nhiều lần trong bộ nhớ. Vì vậy, các giải pháp được đề cập ở trên có vẻ như là một lựa chọn rất tốt. Trong thực tế cho dự án hiện tại của chúng tôi lừa này đã làm rất nhiều ... Bộ nhớ tiêu thụ từ 800mb xuống 44mb, đó là một tác động thực sự rất lớn. Rất tiếc, giải pháp này có chi phí mà tôi muốn hiển thị ở đây và hy vọng tìm cách tránh nó trong khi vẫn sử dụng SharedResourceDictionary.Rò rỉ bộ nhớ khi sử dụng SharedResourceDictionary

Tôi đã tạo một ví dụ nhỏ để trực quan hóa vấn đề bằng từ điển tài nguyên được chia sẻ.

Chỉ cần tạo một ứng dụng WPF đơn giản. Thêm một nguồn 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> 

Bây giờ thêm một usercontrol. Codebehind chỉ là mặc định, vì vậy tôi chỉ hiển thị XAML

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> 

Mã Window đằng sau trông giống như sau

Window1.xaml.cs

// [ ... ] 
    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); 
    } 

Và cửa sổ xaml như thế này

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> 

Tôi biết chương trình là không hoàn hảo và propably thể được thực hiện dễ dàng hơn nhưng trong khi tìm ra một cách để hiển thị các vấn đề này là những gì tôi đã đưa ra. Dù sao:

Bắt đầu điều này và bạn kiểm tra mức tiêu thụ bộ nhớ, nếu bạn có trình thu thập bộ nhớ, việc này trở nên dễ dàng hơn nhiều. Thêm (với hiển thị nó bằng cách nhấp vào tab) và xóa một trang và bạn sẽ thấy mọi thứ hoạt động tốt. Không có gì rò rỉ. Bây giờ, trong mục UserControl.Resources, hãy sử dụng SharedResourceDictionary thay vì ResourceDictionary để bao gồm Shared.xaml. Bạn sẽ thấy rằng MyUserControl sẽ được lưu trong bộ nhớ sau khi bạn xóa một trang và MyUserControl trong đó.

Tôi nhận thấy điều này xảy ra với mọi thứ được khởi tạo qua XAML như trình chuyển đổi, điều khiển người dùng, vv Điều lạ lùng sẽ không xảy ra với Kiểm soát tùy chỉnh. Tôi đoán là, bởi vì không có gì thực sự được khởi tạo trên các điều khiển tùy chỉnh, các mẫu dữ liệu và vân vân.

Vì vậy, trước tiên chúng ta có thể tránh điều đó như thế nào? Trong trường hợp của chúng tôi bằng cách sử dụng SharedResourceDictionary là phải, nhưng rò rỉ bộ nhớ làm cho nó không thể sử dụng nó một cách hiệu quả. Rò rỉ có thể tránh được bằng cách sử dụng CustomControls thay vì UserControls, không phải lúc nào cũng thực tế. Vậy tại sao UserControls được tham chiếu mạnh mẽ bởi ResourceDictionary? Tôi tự hỏi tại sao không ai có kinh nghiệm này trước đây, như tôi đã nói trong một câu hỏi cũ, có vẻ như chúng ta sử dụng từ điển tài nguyên và XAML hoàn toàn sai, nếu không tôi tự hỏi tại sao chúng lại không hiệu quả.

Tôi hy vọng ai đó có thể làm sáng tỏ vấn đề này.

Cảm ơn trước Nico

+0

Trong hồ sơ bộ nhớ của bạn, đối tượng nào giữ tham chiếu đến cá thể MyUserControl? –

+0

Tôi không thể nhớ chắc chắn, nhưng tôi gần như chắc chắn nó là một ResourceDictionary, tốt trong trường hợp của tôi một SharedResourceDictionary. – dowhilefor

Trả lời

5

Tôi không hoàn toàn chắc chắn nếu điều này sẽ giải quyết vấn đề của bạn. Nhưng tôi có các vấn đề tương tự với các điều khiển tham chiếu ResourceDictionary và điều đó liên quan đến việc lười biếng hydration. Đây là một số post trên đó. Và mã này đã giải quyết được sự cố của tôi:

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

Cảm ơn bạn đã liên kết để bắt đầu tìm kiếm một số thông tin chi tiết. Điều này đã giúp tôi. Mặc dù giải pháp thực tế bằng cách duyệt trước tất cả các tài nguyên không chính xác là một lựa chọn cho chúng tôi, bởi vì chúng tôi có nhiều tài nguyên yên tĩnh (với một vài tài nguyên bị hỏng). Tôi sẽ đánh dấu câu trả lời của bạn là câu trả lời hay nhất nếu không có gì tốt hơn. – dowhilefor

9

Tôi đang chạy cùng một vấn đề cần thư mục tài nguyên được chia sẻ trong dự án WPF lớn. Đọc bài viết nguồn và các bình luận, tôi kết hợp một vài bản sửa lỗi cho lớp SharedDirectory như được gợi ý trong các chú thích, dường như đã loại bỏ tham chiếu mạnh (được lưu trữ trong _sourceUri) và cũng làm cho nhà thiết kế hoạt động chính xác. Tôi đã thử nghiệm ví dụ của bạn và nó hoạt động, cả trong nhà thiết kế và MemProfiler chú ý thành công không có tham chiếu được tổ chức. Tôi muốn biết liệu có ai đó đã cải tiến thêm nữa không, nhưng đây là những gì tôi sẽ làm bây giờ:

+3

Một phần mở rộng thú vị cho phương pháp này sử dụng 'WeakReference' để các đối tượng' ResourceDictionary' có thể được thu thập rác khi không sử dụng. Xem [bài đăng trên blog này] (https://codeblitz.wordpress.com/2010/08/25/resourcedictionary-use-with-care/) để biết chi tiết. –

+0

Drew, bạn đã từng kiểm tra xem liệu bộ sưu tập rác có hoạt động không? – tofutim