避坑:.NET記憶體泄露的幾種情況

来源:https://www.cnblogs.com/xbhp/archive/2023/07/03/17522450.html
-Advertisement-
Play Games

記憶體“泄露”是開發中常見的問題之一,它會導致應用程式占用越來越多的記憶體資源,最終可能導致系統性能下降甚至崩潰。軟體開發者需要瞭解在程式中出現記憶體泄露的情況,以避免軟體出現該的問題。 **什麼是記憶體“泄露”?** 記憶體泄露是申請了記憶體空間的變數一直在占用,無法釋放。比如申請了一塊記憶體空間,沒有回收一直 ...


記憶體“泄露”是開發中常見的問題之一,它會導致應用程式占用越來越多的記憶體資源,最終可能導致系統性能下降甚至崩潰。軟體開發者需要瞭解在程式中出現記憶體泄露的情況,以避免軟體出現該的問題。

什麼是記憶體“泄露”?

記憶體泄露是申請了記憶體空間的變數一直在占用,無法釋放。比如申請了一塊記憶體空間,沒有回收一直占用,直到最後記憶體溢出。

在.NET應用程式中,可能會出現以下幾種情況導致記憶體泄漏。

1、 對象保持的引用過長

  • 情況:某個對象持有對其他對象的引用,並且該引用沒有被正確釋放。

  • 示例:一個長時間運行的任務中,持有對大量對象的引用,但任務執行完畢後未釋放這些對象的引用。

  • 解決方案:在不再需要對象時,及時釋放對其的引用。確保在任務完成後,所有不再需要的對象都被正確釋放。

public class LongRunningTask
{
    private List<object> objects; // 對象列表

    public LongRunningTask()
    {
        objects = new List<object>();
    }

    public void RunTask()
    {
        // 執行長時間運行的任務
        // 將對象添加到列表中
        objects.Add(new object());
        objects.Add(new object());
        // ...
    }
    //用完後用這個方法釋放對象列表
    public void Cleanup()
    {
        // 在任務完成後清理對象列表
        objects.Clear();
        objects = null; // 釋放對列表對象的引用
    }
}

在上述示例中,LongRunningTask 類代表一個長時間運行的任務,它持有對一些對象的引用。在任務完成後,通過調用 Cleanup() 方法釋放對對象列表的引用,從而允許垃圾回收器回收這些對象。

2、 事件處理未正確解註冊

  • 情況:在應用程式中訂閱了事件,但沒有在不再需要時正確解註冊。

  • 示例:一個對象訂閱了另一個對象的事件,但在對象不再需要時忘記解註冊事件。

  • 解決方案:在不再需要訂閱事件時,確保正確解註冊事件。可以在對象的生命周期結束時,手動調用事件的解註冊方法或使用弱事件模式,以避免事件發佈者持有訂閱者的引用。

public class EventPublisher
{
    public event EventHandler SomeEvent;

    public void PublishEvent()
    {
        // 發佈事件
        SomeEvent?.Invoke(this, EventArgs.Empty);
    }

    public void UnsubscribeEvent(EventHandler handler)
    {
        // 解註冊事件處理程式
        SomeEvent -= handler;
    }
}

public class EventSubscriber
{
    private EventPublisher publisher;

    public EventSubscriber(EventPublisher publisher)
    {
        this.publisher = publisher;
        // 訂閱事件
        publisher.SomeEvent += HandleEvent;
    }

    private void HandleEvent(object sender, EventArgs e)
    {
        // 處理事件
    }

    public void UnsubscribeFromEvent()
    {
        // 解註冊事件處理程式
        publisher.UnsubscribeEvent(HandleEvent);
    }
}

在上述示例中,EventPublisher 類發佈了一個事件 SomeEvent,EventSubscriber 類訂閱了該事件。通過調用 UnsubscribeFromEvent() 方法,解註冊事件處理程式,從而釋放對事件發佈者的引用。

3、長時間運行的後臺任務:

  • 情況:應用程式中存在長時間運行的後臺任務,這些任務持有對其他對象的引用,並且這些引用沒有被正確釋放。

  • 示例:一個後臺線程持續運行並持有對大量對象的引用,但這些對象在任務完成後不再需要。

  • 解決方案:在後臺任務完成後,及時釋放對其他對象的引用。可以通過在任務執行完畢後手動解除引用,或使用非同步編程模型,確保任務完成後自動釋放引用。

public class BackgroundTask
{
    private CancellationTokenSource cancellationTokenSource;

    public void StartTask()
    {
        cancellationTokenSource = new CancellationTokenSource();

        Task.Run(() =>
        {
            // 長時間運行的後臺任務
            while (!cancellationTokenSource.Token.IsCancellationRequested)
            {
                // 執行任務邏輯
            }
        }, cancellationTokenSource.Token);
    }

    public void StopTask()
    {
        cancellationTokenSource?.Cancel();
        cancellationTokenSource?.Dispose();
        cancellationTokenSource = null; // 釋放對 CancellationTokenSource 對象的引用
    }
}

在上述示例中,BackgroundTask 類代表一個長時間運行的後臺任務。通過調用 StartTask() 方法啟動任務,併在適當的時候調用 StopTask() 方法停止任務。在停止任務時,通過取消 CancellationTokenSource 對象來結束任務,並釋放對該對象的引用。

4、大對象沒有被正確釋放

  • 情況:大對象(如大型數組、大型集合等)占用大量記憶體,但在不再需要時沒有被正確釋放。

  • 示例:一個應用程式在運行過程中創建了大量大型對象,但這些對象在使用後未被正確釋放。

  • 解決方案:在使用完大對象後,及時釋放不再需要的部分或整個對象。可以使用`Dispose`方法或使用`using`語句來確保資源的正確釋放。

public void ProcessLargeData()
{
    byte[] largeData = new byte[100000000]; // 創建一個大型數組
    // 處理大型數據
    // ...

    // 使用完大型數組後,及時釋放
    largeData = null;
}

在上述示例中,創建了一個大型數組 largeData 來存儲大量數據。在處理完數據後,通過將 largeData 設置為 null,釋放對大型數組的引用,從而允許垃圾回收器回收該數組所占用的記憶體。

5、 不正確使用IDisposable介面

  • 情況:在使用實現了IDisposable介面的對象時,沒有正確調用`Dispose`方法來釋放資源。

  • 示例:一個對象實現了IDisposable介面,但在使用完對象後忘記調用`Dispose`方法。

  • 解決方案:在使用完實現了IDisposable介面的對象後,使用`using`語句或手動調用`Dispose`方法來釋放資源。確保正確地管理實現了IDisposable介面的對象。

public class CustomResource : IDisposable
{
    private bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // 釋放托管資源
            }

            // 釋放非托管資源
            // ...

            disposed = true;
        }
    }

    ~CustomResource()
    {
        Dispose(false);
    }
}
//歡迎關註公眾號:DOTNET開發跳槽,領取海量面試題。加微信號xbhpnet入群交流

在上述示例中,CustomResource 類實現了 IDisposable 介面。在 Dispose() 方法中,通過調用 Dispose(true) 來釋放托管資源,通過調用 Dispose(false) 來釋放非托管資源。在 CustomResource 類的析構函數中,調用 Dispose(false) 來確保資源的釋放。使用時,應該在不再需要 CustomResource 對象時調用 Dispose() 方法,或使用 using 語句來自動釋放資源。

結語

請註意,以上示例僅用於說明可能的記憶體泄漏情況和解決方案,並不一定適用於所有具體的應用程式。在實際開發中,應根據應用程式的特性和需求,仔細審查代碼並確保正確的資源管理和釋放,以避免記憶體泄漏問題的出現。

以上只列舉了幾種情況,還有其它情況,比如在代碼中使用了靜態變數也容易導致記憶體泄露。希望本文對你有所收穫,歡迎留言和吐槽。

版權聲明:本文來源於網友收集或網友供稿,僅供學習交流之用,如果有侵權,請轉告小編或者留言,本公眾號立即刪除。


來源公眾號:DotNet開發跳槽

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 本文是《JavaCV的攝像頭實戰》系列的 ...
  • # 1、什麼是MapReduce 1. Hadoop MapReduce 是一個 `分散式計算框架`,用於輕鬆編寫分散式應用程式,這些應用程式以可靠,容錯的方式並行處理大型硬體集群(數千個節點)上的大量數據(多TB數據集) 2. MapReduce 是一種`面向海量數據`處理的一種指導思想,也是一種 ...
  • # Cookie對象 Cookie是瀏覽器提供的一種技術,通過伺服器的程式能將一些只須保存在客戶端,或者在客戶端進行處理的數據,放在本地的電腦上,不需要通過網路傳輸,因而提高網頁處理的效率,並且能夠減少伺服器的負載,但是由於Cook是伺服器端保存在客戶端的信息,所以其安全性也是很差的。例如常見的記 ...
  • `numpy` 數組通常是用於數值計算的多維數組,而排序功能可以快速、準確地對數據進行排序,從而得到更加清晰、易於分析的結果。 在數據分析和處理過程中,常常需要對數據進行排序,以便更好地理解和發現其中的規律和趨勢。 排序會應用在很多場景中,比如: 1. 數據分類:將數據按照一定的特征進行分類,可以通 ...
  • ## 前言 閱讀此篇前,可先閱讀[尾碼數組](https://www.cnblogs.com/pdpdzaa/p/17436993.html) ## LCP ```LCP``` 就是最長公共首碼,在尾碼數組中,$LCP(i,j)$ 就代表從 $sa_i$ 開始的尾碼和從 $sa_j$ 開始的尾碼的最 ...
  • 我在不要更新挑戰中堅持了一年🎉🎉🎉,你也來試試吧(咕咕咕)! 好言歸正傳,本次更新帶來的是經典游戲掃雷,基於JavaFX實現。篇幅有限,文章主要介紹核心操作實現,不會列出所有代碼。需要完整源碼或是想預覽最終效果,可以點擊下方鏈接。後續會逐步更新細節實現方面的內容,將來吧反正(肯定不鴿!) 視頻 ...
  • ## 背景 隨著所在公司的發展,應用服務的規模不斷擴大,原有的垂直應用架構已無法滿足產品的發展,幾十個工程師在一個項目里並行開發不同的功能,開發效率不斷降低。 於是公司開始全面推進服務化進程,把團隊內的大部分工程師主要精力全部都集中到服務化中。服務化可以讓每個工程師僅在自己負責的子項目中進行開發,提 ...
  • 寫java時不管是我們自己new對象還是spring管理bean,儘管我們天天跟對象打交道,那麼對象的結構和記憶體佈局有多少人知道呢,這篇文章可帶你入門,瞭解java對象記憶體佈局。 本文涉及到JVM指針壓縮的知識點,不熟悉的小伙伴可以看前面寫過的一篇關於指針壓縮的文章。 [JVM之指針壓縮](http ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...