通過減少記憶體使用改善.NET效能
對.NET的效能調優來說,我們有一個普遍被誤解的觀念:規避記憶體分配的重要性。人們認為,由於記憶體分配是快速的,因此很少會對效能產生影響。
\\要理解導致這種誤解的原因,我們必須回到在C++和Visual Basic 4到6中所看到的COM程式設計時代。對於COM,記憶體是使用引用計數形式的垃圾回收器進行管理的。每當將一個物件分配給一個引用變數時,就會增加一個隱藏的計數器。如果變數被重新分配或從作用域退出,計數器就會被取消。如果計數器達到0,物件就會被刪除,將記憶體釋放到其他地方。
\\這種記憶體管理系統是“確定的”。通過仔細分析,你可以確定何時刪除一個物件。這意味著你可以自動釋放資料庫連線等資源。而對於.NET而言,你需要一個單獨的機制(例如,銷燬/啟用)以確保非記憶體資源能夠及時地被釋放。
\\引用計數垃圾收集器有三個主要的缺點。首先,它們容易受到“迴圈引用”的影響。如果兩個物件相互引用,即使是間接的,那麼引用計數也不可能降為0,這便會導致記憶體洩漏的發生。我們必須小心地編寫程式碼,要麼避免迴圈引用,要麼提供某種解構方法以便在當物件不再需要時中斷迴圈。
\\工作在多執行緒環境中時會遇到另一個主要的缺點。為了避免競態條件,某種型別的鎖機制(例如:鎖住、增量、旋鎖等)需要確保重新計數仍然是正確的。這些操作出奇的昂貴。
\\最後,可用記憶體位置的列表可能會變成碎片化的,在活動物件之間會產生許多小的、不可用的空間。記憶體分配通常涉及到遍歷一個有空閒空間的連續連結串列,以便尋找到一個足夠大的位置來滿足需求物件。(記憶體碎片在.NET中也存在於“大物件堆”或“LOH”。)
\\相比之下,像.NET或Java那樣將記憶體分配到一個“標記-清掃”形式的垃圾回收器中,便是一個簡單的指標增量機制。賦值並不比分配一個整數更昂貴。只有當GC實際執行時,才會支付實際成本,而且通常通過使用分代收集器來緩解這種情況。
\\當.NET剛出現的時候,許多人抱怨.NET的垃圾回收器不確定性的表現將會損害效能並且難以解釋。當時微軟的反駁是,對於大多數用例來說,儘管間歇的GC會暫停,但“標記-清掃”的垃圾回收器實際上會更快。
\\不幸的是,隨著時間的推移,這條資訊變得有些混亂。即使我們接受這樣一種理論,即“標記-清掃”的垃圾回收器速度比引用計數更快,但這並不意味著它在絕對意義上是必須的。記憶體分配和相關的記憶體壓力通常是很難檢測效能問題的原因。
\\而且,使用的記憶體越多,CPU快取的效率就越低。雖然主RAM很大,以至於在大多數用例中幾乎不會使用到基於磁碟的虛擬記憶體,但是相比之下,CPU中的快取是很小的。從RAM中填充CPU快取所需的時間可能會佔用數十甚至數百個CPU週期。
\\在最近的一篇文章中,Frans Bouma確定了幾種優化記憶體使用的技術。雖然他著重關注改善ORM效能,但這些建議在各種情況下都很有用。他的這些建議包括:
\\避免引數陣列
\\引數關鍵字是有用處的,但與普通的函式呼叫相比要昂貴,因為它需要記憶體分配。API應該為常用的引數計算提供無引數過載。
\\還應該提供一個IEnumerable\u0026lt;T\u0026gt; 或者IList\u0026lt;T\u0026gt;的過載,這樣集合在呼叫函式之前就不需要多此一舉的被複制到一個陣列中了。
\\如果定義之後立即新增資料,可先預定義資料結構的大小
\\List\u0026lt;T\u0026gt;或其他集合類可以在被填充時多次調整大小。每次調整大小的操作都會分配另一個內部陣列,並由前一個陣列填充。你可以通過為集合的建構函式提供一個容量引數來避免這種開銷。
\\惰性的初始化成員
\\如果你知道一個給定物件在大多數情況下是不需要的,那麼你應該使用延遲初始化來避免過早地分配記憶體給它。通常這是手動完成的,因為Lazy\u0026lt;T\u0026gt; 類本身需要分配記憶體。
\\早在2011年,我們就曾報導過微軟試圖通過使用類似技術來減少任務的規模。他們的報告顯示,在建立一個\u0026lt;int32\u0026gt;任務的時候花費的時間減少了49%到55%,所需空間大小減少了52%。
\\檢視英文原文:Improving .NET Performance by Reducing Memory Usage
\\感謝冬雨對本文的審校。
\給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家通過新浪微博(@InfoQ,@丁曉昀),微信(微訊號:InfoQChina)關注我們。
相關文章
- 使用String.intern減少記憶體使用記憶體
- 通過減少動態派送提升效能
- 谷歌改善 Chrome 記憶體安全:通過 heap scanning 演算法減少 C++ 程式碼庫安全漏洞谷歌Chrome記憶體演算法C++
- curl 中減少記憶體分配操作記憶體
- win10系統下如何減少RAM記憶體使用Win10記憶體
- 減少.NET應用程式記憶體佔用的一則實踐記憶體
- Effective C#:儘量減少記憶體垃圾C#記憶體
- 字串池化,減少1/3記憶體佔用字串記憶體
- 谷歌Chrome瀏覽器引入省記憶體/省電模式:減少記憶體佔用谷歌Chrome瀏覽器記憶體模式
- python使用迭代生成器yield減少記憶體佔用的方法Python記憶體
- C# 使用繫結控制程式碼來減少程式的記憶體耗用C#記憶體
- 微軟的HotSpot C2可減少15%堆記憶體分配微軟HotSpot記憶體
- python定時爬蟲啟用時如何減少記憶體?Python爬蟲記憶體
- GO語言————6.12 通過記憶體快取來提升效能Go記憶體快取
- 使用 Node.js Stream API 減少伺服器端記憶體消耗的一個具體例子Node.jsAPI伺服器記憶體
- php簡單使用shmop函式建立共享記憶體減少伺服器負載PHP函式記憶體伺服器負載
- 改善SQL Server記憶體管理(轉)SQLServer記憶體
- 如何通過 WebP 相容減少圖片資源大小Web
- Mqttnet記憶體與效能改進錄MQQT記憶體
- 減少Spring Boot的JVM記憶體佔用的Docker三種配置Spring BootJVM記憶體Docker
- 通過基準配置檔案改善應用效能
- GoldenGate通過CACHEMGR限制記憶體利用Go記憶體
- 選擇合適Redis資料結構,減少80%的記憶體佔用Redis資料結構記憶體
- Swift Instruments工具的使用,通過leaks分析記憶體洩露Swift記憶體洩露
- [譯] Swift:通過示例避免記憶體洩漏Swift記憶體
- Composer 2 真的可以減少或規避記憶體不夠的致命錯誤記憶體
- 揭秘!Vue3.5響應式重構如何讓記憶體佔用減少56%Vue記憶體
- iOS 使用Instruments優化記憶體效能iOS優化記憶體
- Oracle 記憶體使用建議效能檢視Oracle記憶體
- 使用MVVM減少控制器程式碼實戰(減少56%)MVVM
- 記憶體使用過高點檢checklist記憶體
- 記憶體效能分析工具記憶體
- 如何通過 WebP 自適應方案減少圖片資源大小Web
- windows下可以通過使用orastack工具修改每個thread使用的stack記憶體WindowsASTthread記憶體
- 實時渲染不是夢:通過共享記憶體優化Flutter外接紋理的渲染效能記憶體優化Flutter
- Linux程式間通訊——使用共享記憶體Linux記憶體
- ORACLE 使用超過2G記憶體Oracle記憶體
- C# .Net 多程式同步 通訊 共享記憶體 記憶體對映檔案 Memory MappedC#記憶體APP