日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

WPF中解决内存泄露的几点提示与解决方法

發(fā)布時間:2023/11/30 asp.net 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WPF中解决内存泄露的几点提示与解决方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一直以來用WPF做一個項目,但是開發(fā)中途發(fā)現(xiàn)內(nèi)存開銷太大,用ANTS Memory Profiler分析時,發(fā)現(xiàn)在來回點幾次載入頁面的操作中,使得非托管內(nèi)存部分開銷從起始的43.59M一直到150M,而托管部分的開銷也一直持高不下,即每次申請的內(nèi)存在結(jié)束后不能完全釋放。在網(wǎng)上找了不少資料,甚受益,現(xiàn)在修改后,再也不會出現(xiàn)這種現(xiàn)象了(或者說,即使有也不嚇人),寫下幾個小心得:

1. 慎用WPF樣式模板合并

  我發(fā)現(xiàn)不采用合并時,非托管內(nèi)存占用率較小,只是代碼的理解能力較差了,不過我們還有文檔大綱可以維護(hù)。

2. WPF樣式模板請共享

  共享的方式最簡單不過的就是建立一個類庫項目,把樣式、圖片、筆刷什么的,都扔進(jìn)去,樣式引用最好使用StaticResource,開銷最小,但這樣就導(dǎo)致了一些寫作時的麻煩,即未定義樣式,就不能引用樣式,哪怕定義在后,引用在前都不行。

3. 慎用隱式類型var的弱引用

  這個本來應(yīng)該感覺沒什么問題的,可是不明的是,在實踐中,發(fā)現(xiàn)大量采用var與老老實實的使用類型聲明的弱引用對比,總是產(chǎn)生一些不能正確回收的WeakRefrense(這點有待探討,因為開銷不是很大,可能存在一些手工編程的問題)

4. 寫一個接口約束一下

  誰申請誰釋放,基本上這點能保證的話,內(nèi)存基本上就能釋放干凈了。我是這么做的:

interface IUIElement : IDisposable
{
/// <summary>
/// 注冊事件
/// </summary>
void EventsRegistion();

/// <summary>
/// 解除事件注冊
/// </summary>
void EventDeregistration();
}

在實現(xiàn)上可以這樣:

1 #region IUIElement 成員
2 public void EventsRegistion()
3 {
4 this.traineeReport.SelectionChanged += new SelectionChangedEventHandler(traineeReport_SelectionChanged);
5 }
6
7 public void EventDeregistration()
8 {
9 this.traineeReport.SelectionChanged -= new SelectionChangedEventHandler(traineeReport_SelectionChanged);
10 }
11
12 private bool disposed;
13
14 ~TraineePaymentMgr()
15 {
16 ConsoleEx.Log("{0}被銷毀", this);
17 Dispose(false);
18 }
19
20 public void Dispose()
21 {
22 ConsoleEx.Log("{0}被手動銷毀", this);
23 Dispose(true);
24 GC.SuppressFinalize(this);
25 }
26
27 protected void Dispose(bool disposing)
28 {
29 ConsoleEx.Log("{0}被自動銷毀", this);
30 if(!disposed)
31 {
32 if(disposing)
33 {
34 //托管資源釋放
35 ((IDisposable)traineeReport).Dispose();
36 ((IDisposable)traineePayment).Dispose();
37 }
38 //非托管資源釋放
39 }
40 disposed = true;
41 }
42 #endregion

?比如寫一個UserControl或是一個Page時,可以參考以上代碼,實現(xiàn)這樣接口,有利于資源釋放。

5. 定時回收垃圾

DispatcherTimer GCTimer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
this.GCTimer.Interval = TimeSpan.FromMinutes(10); //垃圾釋放定時器 我定為每十分鐘釋放一次,大家可根據(jù)需要修改   this.GCTimer.start();

this.EventsRegistion(); // 注冊事件
}

public void EventsRegistion()
{
this.GCTimer.Tick += new EventHandler(OnGarbageCollection);
}

public void EventDeregistration()
{
this.GCTimer.Tick -= new EventHandler(OnGarbageCollection);
}

void OnGarbageCollection(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}

6. 較簡單或可循環(huán)平鋪的圖片用GeometryDrawing實現(xiàn)

一個圖片跟幾行代碼相比,哪個開銷更少肯定不用多說了,而且這幾行代碼還可以BaseOn進(jìn)行重用。

<DrawingGroup x:Key="Diagonal_50px">
<DrawingGroup.Children>
<GeometryDrawing Brush="#FF2A2A2A" Geometry="F1 M 0,0L 50,0L 50,50L 0,50 Z"/>
<GeometryDrawing Brush="#FF262626" Geometry="F1 M 50,0L 0,50L 0,25L 25,0L 50,0 Z"/>
<GeometryDrawing Brush="#FF262626" Geometry="F1 M 50,25L 50,50L 25,50L 50,25 Z"/>
</DrawingGroup.Children>
</DrawingGroup>

這邊是重用

<DrawingBrush x:Key="FrameListMenuArea_Brush" Stretch="Fill" TileMode="Tile" Viewport="0,0,50,50" ViewportUnits="Absolute" Drawing="{StaticResource Diagonal_50px}"/>

上面幾行代碼相當(dāng)于這個:

7. 使用Blend做樣式的時候,一定要檢查完成的代碼

眾所周知,Blend定義樣式時,產(chǎn)生的垃圾代碼還是比較多的,如果使用Blend,一定要檢查生成的代碼。

?

8. 靜態(tài)方法返回諸如List<>等變量的,請使用out

比如

public static List<String> myMothod()

{...}

請改成

public static myMothod(out List<String> result)

{...}

?

9. 打針對此問題的微軟補丁

3.5的應(yīng)該都有了吧,這里附上NET4的內(nèi)存泄露補丁地址,下載點這里?(QFE:? Hotfix request to implement hotfix KB981107 in .NET 4.0?)

這是官方給的說明,看來在樣式和數(shù)據(jù)綁定部分下了點工夫啊:

  • 運行一個包含樣式或模板,請參閱通過使用 StaticResource 標(biāo)記擴(kuò)展或 DynamicResource 標(biāo)記擴(kuò)展應(yīng)用程序資源的 WPF 應(yīng)用程序。 創(chuàng)建使用這些樣式或模板的多個控件。 但是,這些控件不使用引用的資源。 在這種情況的一些內(nèi)存WeakReference對象和空間泄漏的控股數(shù)組后,垃圾回收釋放該控件。
  • 運行一個包含的控件的屬性是數(shù)據(jù)綁定到的 WPF 應(yīng)用程序DependencyObject對象。 該對象的生存期是超過控件的生存期。 許多控件時創(chuàng)建,一些內(nèi)存WeakReference對象和容納數(shù)組空格被泄漏后垃圾回收釋放該控件。
  • 運行使用樹視圖控件或控件派生于的 WPF 應(yīng)用程序,選擇器類。 將控件注冊為控制中的鍵盤焦點的內(nèi)部通知在KeyboardNavigation類。 該應(yīng)用程序創(chuàng)建這些控件的很多。 例如對于您添加并刪除這些控件。 在本例中為某些內(nèi)存WeakReference對象和容納數(shù)組空格被泄漏后垃圾回收釋放該控件。
  • 繼續(xù)更新有關(guān)的三個8月補丁,詳細(xì)的請百度:KB2487367??KB2539634??KB2539636,都是NET4的補丁,在發(fā)布程序的時候,把這些補丁全給客戶安裝了會好的多。

    10.? 對string怎么使用的建議

    這個要解釋話就長了,下面僅給個例子說明一下,具體的大家去找找MSDN

    string ConcatString(params string[] items)
    {
    string result = "";
    foreach (string item in items)
    {
    result
    += item;
    }
    return result;
    }

    string ConcatString2(params string[] items)
    {
    StringBuilder result
    = new StringBuilder();
    for(int i=0, count = items.Count(); i<count; i++)
    {
    result.Append(items[i]);
    }
    return result.ToString();
    }

    建議在需要對string進(jìn)行多次更改時(循環(huán)賦值、連接之類的),使用StringBuilder。我已經(jīng)把工程里這種頻繁且大量改動string的操作全部換成了StringBuilder了,用ANTS Memory Profiler分析效果顯著,不僅提升了性能,而且垃圾也少了。

    ?

    11. 其它用上的技術(shù)暫時還沒想到,再補充...

    ?

    如果嚴(yán)格按以上操作進(jìn)行的話,可以得到一個滿意的結(jié)果:

    運行了三十分鐘,不斷的切換功能,然后休息5分鐘,回頭一看,結(jié)果才17M左右內(nèi)存開銷,效果顯著吧。

    然后對于調(diào)試信息的輸出,我的做法是在窗體應(yīng)用程序中附帶一個控制臺窗口,輸出調(diào)試信息,給一個類,方便大家:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;

    namespace Trainee.UI.UIHelper
    {
    public struct COORD
    {
    public ushort X;
    public ushort Y;
    };

    public struct CONSOLE_FONT
    {
    public uint index;
    public COORD dim;
    };

    public static class ConsoleEx
    {
    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32", CharSet = CharSet.Auto)]
    internal static extern bool AllocConsole();

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32", CharSet = CharSet.Auto)]
    internal static extern bool SetConsoleFont(IntPtr consoleFont, uint index);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32", CharSet = CharSet.Auto)]
    internal static extern bool GetConsoleFontInfo(IntPtr hOutput, byte bMaximize, uint count, [In, Out] CONSOLE_FONT[] consoleFont);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32", CharSet = CharSet.Auto)]
    internal static extern uint GetNumberOfConsoleFonts();

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32", CharSet = CharSet.Auto)]
    internal static extern COORD GetConsoleFontSize(IntPtr HANDLE, uint DWORD);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32.dll ")]
    internal static extern IntPtr GetStdHandle(int nStdHandle);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern int GetConsoleTitle(String sb, int capacity);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "user32.dll", EntryPoint = "UpdateWindow")]
    internal static extern int UpdateWindow(IntPtr hwnd);

    [System.Security.SuppressUnmanagedCodeSecurity]
    [DllImport(
    "user32.dll")]
    internal static extern IntPtr FindWindow(String sClassName, String sAppName);

    public static void OpenConsole()
    {
    var consoleTitle
    = "> Debug Console";
    AllocConsole();


    Console.BackgroundColor
    = ConsoleColor.Black;
    Console.ForegroundColor
    = ConsoleColor.Cyan;
    Console.WindowWidth
    = 80;
    Console.CursorVisible
    = false;
    Console.Title
    = consoleTitle;
    Console.WriteLine(
    "DEBUG CONSOLE WAIT OUTPUTING...{0} {1}\n", DateTime.Now.ToLongTimeString());

    try
    {
    //這里是改控制臺字體大小的,可能會導(dǎo)致異常,在我這個項目中我懶得弄了,如果需要的的話把注釋去掉就行了
    //IntPtr hwnd = FindWindow(null, consoleTitle);
    //IntPtr hOut = GetStdHandle(-11);

    //const uint MAX_FONTS = 40;
    //uint num_fonts = GetNumberOfConsoleFonts();
    //if (num_fonts > MAX_FONTS) num_fonts = MAX_FONTS;
    //CONSOLE_FONT[] fonts = new CONSOLE_FONT[MAX_FONTS];
    //GetConsoleFontInfo(hOut, 0, num_fonts, fonts);
    //for (var n = 7; n < num_fonts; ++n)
    //{
    // //fonts[n].dim = GetConsoleFontSize(hOut, fonts[n].index);
    // //if (fonts[n].dim.X == 106 && fonts[n].dim.Y == 33)
    // //{
    // SetConsoleFont(hOut, fonts[n].index);
    // UpdateWindow(hwnd);
    // return;
    // //}
    //}
    }
    catch
    {

    }
    }

    public static void Log(String format, params object[] args)
    {
    Console.WriteLine(
    "[" + DateTime.Now.ToLongTimeString() + "] " + format, args);
    }
    public static void Log(Object arg)
    {
    Console.WriteLine(arg);
    }
    }
    }

    在程序啟動時,可以用ConsoleEx.OpenConsole()打開控制臺,用ConsoleEx.Log(.....)或者干脆用Console.WriteLine進(jìn)行輸出就可以了。

    轉(zhuǎn)載于:https://www.cnblogs.com/LastPropose/archive/2011/08/01/2124359.html

    總結(jié)

    以上是生活随笔為你收集整理的WPF中解决内存泄露的几点提示与解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。