C# 客戶端內存優化分析

背景概述

C# 開發客戶端系統的時候,.net 框架本身就比較消耗內存資源,特別是xp 這種老爺機內存配置不是很高的電腦上運行,所以就需要進行內存上的優化,才能流暢的在哪些低端電腦上運行. 想要對C# 開發的客戶端內存優化需要了解以下幾個概念。

虛擬內存

這裏引用百度百科的概念:虛擬內存是計算機系統內存管理的一種技術。它使得應用程序認爲它擁有連續的可用的內存(一個連續完整的地址空間),而實際上,它通常是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數據交換。目前,大多數操作系統都使用了虛擬內存,如Windows家族的“虛擬內存”;Linux的“交換空間”等。

一句話概括虛擬內存既是使用磁盤,物理磁盤進行虛擬化出來的內存空間。

物理內存

物理內存(Physical memory)是相對於虛擬內存而言的。物理內存指通過物理內存條而獲得的內存空間,而虛擬內存則是指將硬盤的一塊區域劃分來作爲內存。內存主要作用是在計算機運行時爲操作系統和各種程序提供臨時儲存。常見的物理內存規格有256M、512M、1G、2G等,現如今隨着計算機硬件的發展,已經出現4G、8G甚至更高容量的內存規格。當物理內存不足時,可以用虛擬內存代替。在應用中,自然是顧名思義,物理上,真實存在的插在主板內存槽上的內存條的容量的大小。看計算機配置的時候,主要看的就是這個物理內存。

GC 垃圾回收機制

簡介

C#中和Java一樣是一種系統自動回收釋放資源的語言,在C#環境中通過 GC(Garbage Collect)進行系統資源回收,在數據基本類型中介紹到,C#數據類型分爲引用類型和值類型,

值類型保存在Stack上,隨着函數的執行作用域執行完畢而自動出棧,所以這一類型的資源不是GC所關心 對象。GC垃圾回收主要是是指保存在Heap上的資源。

.NET的GC機制有這樣兩個問題:

  • 首先,GC並不是能釋放所有的資源。它不能自動釋放非託管資源。
  • 第二,GC並不是實時性的,這將會造成系統性能上的瓶頸和不確定性。
GC並不是實時性的,這會造成系統性能上的瓶頸和不確定性。所以有了IDisposable接口,IDisposable接口定義了Dispose方法,這個方法用來供程序員顯式調用以釋放非託管資源。使用using語句可以簡化資源管理。

託管資源和非託管資源

上面介紹到,GC只釋放託管資源,那麼什麼是託管資源和費託管資源。

  • 託管資源 : 託管資源指的是.NET可以自動進行回收的資源,主要是指託管堆上分配的內存資源。託管資源的回收工作是不需要人工干預的,有.NET運行庫在合適調用垃圾回收器進行回收。

  • 非託管資源:非託管資源指的是.NET不知道如何回收的資源,最常見的一類非託管資源是包裝操作系統資源的對象,例如文件,窗口,網絡連接,數據庫連接,畫刷,圖標 等。這類資源,

垃圾回收器在清理的時候會調用Object.Finalize()方法。默認情況下,方法是空的,對於非託管對象,需要在此方法中編寫回收非託管資源的代碼,以便垃圾回收器正確回收資源。

  • 總結:託管資源是釋放由GC來完成,釋放的時間吧不一定,一般是系統感覺內存喫緊,會進行緊急回收資源。一個對象想成爲被回收,首先需要成爲垃圾,GC是通過判斷對象及其子對象有沒有指向有效的引用,
    如果沒有GC就認爲它是垃圾。垃圾回收機制通過一定的算法得到哪些沒有被被引用、或者不再調用的資源,當這些垃圾達到一定的數量時,回啓動垃圾回收機制,GC回收實際上是調用了析構函數。
    垃圾回收機制意味着你不需要擔心處理不再需要的對象了。咱們關心的主要是非託管資源的釋放。
    垃圾回收時對象一共有三代 :0,1,2。每一代都有自己的內存預算,空間不足的時候會調用垃圾回收。爲了提高性能都是按代回收,第0代超預算之後就回收第0代的對象,而存活下來的對象就提升爲第1代,
    依次類推,而往往經過多次0代的垃圾回收才能回收一次第1代。

GC進行垃圾回收是系統決定的,下面是進行強制回收的執行代碼(非特殊情況下不要使用此方法,會影響系統效率,削弱垃圾回收器中優化引擎的作用,而垃圾回收器可以確定運行垃圾回收的最佳時間)

//對所有代進行垃圾回收。
GC.Collect();
//對指定的代進行垃圾回收。
GC.Collect(int generation); 
//強制在 System.GCCollectionMode 值所指定的時間對零代到指定代進行垃圾回收。
GC.Collect(int generation, GCCollectionMode mode); 

關於 SetProcessWorkingSetSize 和內存釋放

在應用程序中,往往爲了釋放內存等,使用一些函數,其實,對於內存操作函數要謹慎使用,比如大家常常想到的 SetProcessWorkingSetSize,其實對於windows來說,系統會自動在程序閒置時(如程序被最小化)釋放內存的,自己用內存釋放 時,往往會造成一些莫名的內存錯誤,造成自己的應用程序及系統不穩定。

SetProcessWorkingSetSize的作用

  • 物理內存轉移到虛擬內存中
    • msdn解釋:使用這個函數來設置應用程序最小和最大的運行空間,只會保留需要的內存.當應用程序被閒置或系統內存太低時,操作系統會自動調用這個機制來設置應用程序的內存.應用程序也可以使用 VirtualLock 來鎖住一定範圍的內存不被系統釋放;當你加大運行空間給應用程序,你能夠得到的物理內存取決於系統,這會造成其他應用程序降低性能或系統總體降低性能,這也可能導致請求物理內存的操作失敗,例如:建立 進程,線程,內核池,就必須小心的使用該函數.也就是說,該函數不是節省內存,而是強制把進程的物理內存搬到虛擬內存中.

SetProcessWorkingSetSize 的劣勢

  • 危害:如果 SetProcessWorkingSetSize 函數被正常使用,是非常有用處的.但是爲了矇騙用戶的眼睛,每秒,甚至幾十毫秒就把大量內存往虛擬內存裏面壓,就會帶來無可預計的危害.看看這篇文章怎麼 說:“因爲他只是暫時的將應用程序佔用的內存移至虛擬內存,一旦,應用程序被激活或者有操作請求時,這些內存又會被重新佔用.如果你強制使用該方法來設置 程序佔用的內存,那麼可能在一定程度上反而會降低系統性能,因爲系統需要頻繁的進行內存和硬盤間的頁面交換.”.

優化內存代碼:

[DllImport("kernel32.dll")]
private static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);

private void FlushMemory()
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
    {
        SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
    }
}

歡迎各位大佬關注公衆號
dotNET 博士

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章