.Net程序內存異常的原因及解決
一、概要
大概在今年三月份的時候突然被緊急調到另外一個項目組解決線上內存異常問題。經過兩周的玩命奮戰(zhàn)終于解決了這個問題這里把心路歷程及思路分享給大家。希望可以幫助到各位或現在正遇到這樣事情的小伙伴提供一些思路。
二、場景
當部門老大找到我的時候,給我描述了這樣一段話。
“目前服務出現了提交內存異常的問題,目前分析出來可能是日志組件有大量的日志消息堆積把內存占滿導致服務崩潰了。在國內某地區(qū)客戶的服務器上15000臺物聯網設備不能正常工作這個問題非常緊急需要馬上解決?!?/p>
問題描述至此,沒有其他可用信息。這時候我先崩潰了...但是任務找到你不能說不行。萬一解決了這種重大事故還能在部門老大面前秀一把。
三、思路
(1)分析
Part1,分析日志堆積原因
- 拿到服務器地址去翻出日志文件,查看日志內容;內容基本上都是一些報錯情況xxx對象為null,對象轉換失敗。
- 日志組件的實現也比較糟糕Log對象在每個調用的類里都會重新new
解決方案:
- 修復對象為null的問題并加上空值判斷,大概的原因就是json值轉換的時候傳入的值是null那么就引起這兩塊的連鎖反應。非常值得注意的一點是通常json對象轉換的地方都會加入try塊去捕獲異常在程序里try的捕捉是會對.net程序造成性能影響的所以能用判斷規(guī)避的盡量不要去觸發(fā)try機制,程序性能被拖下去其他方面的處理就會變相的削減處理速度變慢那么數據堆積好像就解釋的通了。
- 將日志組件重構為單例且線程安全的實現,寫入日志的數據結構體是class這里改成struct,考慮的因素是引用類型會存在引用問題再就是考慮的值類型和引用類型在內存中占用的大小是不一樣的,而且值類型和引用類型在處理速度上值類型更快。
以為這樣就結束了嗎?不,當程序改好之后放在測試服務器上跑第二天早上測試部的小姐姐就找到我說異常報錯情況是好了,但是內存泄漏還是沒解決。
Part2,查找內存泄漏的根本原因
看來Part1的操作僅僅只是修復了一個小bug而已,并不是我所想的那么簡單,在日志的查看中還發(fā)現log日志中出現“tcp服務拒絕連接XXX異常”。當我看到這些的時候心情糟糕透了....
1.一早我就用Profile把服務程序跑了一遍發(fā)現了
(1)有幾個消息隊列占用非常大,查閱代碼之后發(fā)現服務端程序會和15000臺物聯網設備進行交互的所有數據都會先堆積到這個隊列里如果這個隊列滿了(Queue上限被設定2w)會new新的Queue然后把溢出的部分轉到新的Queue里,最可怕的是從隊列里取數據的還是單線程處理。

(2)還會有很多磁盤I/O的操作會存儲在應用服務器本機上例如socket通訊的報文和需要轉發(fā)的內容等等都會進行寫入操作。
(3)逐步調試的時候發(fā)現大部分的方法實現都是同步方法,而且框架版本居然是.net freamwork4。
解決方案:
(1)
【移除new新隊列的機制、刪除Main Queue的上限設置改為多線程處理Queue;一切數據堆積的本質就是數據處理不過來所以開辟再多的內存空間都是慢性死亡而已?!?br /> 【走訪物聯網硬件部門,詢問物聯網設備發(fā)送數據頻率、設備數、單臺設備發(fā)送單條數據的大小是多少KB;為什么需要了解?這些第一點在程序內記錄日志然后統(tǒng)計成走勢圖能直接觀察隊列內部的變化開會的時候能給領導具有說服力的證據能看到數據量什么時候陡增、數據大小等;第二點因為這些報文數據需要存在應用服務器本地那么這時候就能計算出寫入的數據量有沒有超出普通硬盤的寫入I/O瓶頸以及網絡帶寬的占用?!?br /> 【走訪物聯網硬件部門2,詢問物聯網設備socket傳輸數據時是否有走正?!皌cp揮手”流程;為什么?因為socket tcp通訊中,是雙工通道那么其中有一端突然斷開,另一端會進入“wait”狀態(tài)不會及時回收tcp連接資源,大家試想一下如果15000臺設備高頻短連接去操作那么服務端連接隊列資源很有可能吃不消。這個時候就需要服務端主動斷開“失效”連接及時回收資源“拆除雙工通道”以及調整socket連接隊列大小?!?/p>

(2)磁盤寫入報文信息這塊,就要用三寸不爛之舌說動項目經理把這塊砍掉以節(jié)約CPU性能以及減少磁盤I/O,大伙試想一下每次socket通訊進行收發(fā)的時候都要去操作一下I/O那是多么恐怖的一件事情;最后溝通結果那個組的項目經理同意砍掉部分模塊磁盤寫入功能,那么問題來了剩下的怎么辦如何將優(yōu)勢進一步擴大?這時候繼續(xù)查閱項目代碼,結果發(fā)現socket通訊中“收”、“發(fā)”都會操作一次。那么這時候需要做的是將報文積累到一定數量比如說積累1000條報文再一次性寫入那么磁盤I/O的操作頻率將成倍遞減。

(3)最后一個問題,就是講所有的方法修改為異步方法。這時候就能祭出Task、Async、Await了。但是基于的框架是.net freamwork4的,后來又去查閱MSDN的文檔發(fā)現.net freamwork4遠古框架中還是有這些特性的雖然用法稍微難受點但是還是能優(yōu)化的。一定要記住一點,開發(fā)服務端要有“服務端”思維如果都是同步方法就會被同步阻塞處于“等待處理結果狀態(tài)”這樣的話服務端的并發(fā)量是上不去的。
這里雖然沒怎么用上的一發(fā)大招,但是這里還是分享給大家“注釋大法”;注釋掉最有可能出問題的地方逐一排查一定能發(fā)現問題的所在就是非常的耗時那會我基本每天工作12小時,尤其是公司的遠古項目通?!按a爛”、“設計基本沒有”、“使用的.net框架版本低”等等,一堆惡心人的事情發(fā)生。
(2)工具
- Visual Studio自帶的Profile?!究梢苑治鯟PU、內存等占用情況;這款比較推薦】
- VMMap【可以分析CPU、內存等占用情況】
- ANTS Performance Profiler【這款工具比較強大能分析調用鏈路逐級告訴你內存占用的地方以及內存占用大小】
- Window操作系統(tǒng)自帶的資源監(jiān)視器這個不用多說大家都會用。
Part3,總結
基于以上的修改,在測試服務器上穩(wěn)定運行3周內存穩(wěn)定在2.9G左右;
一定要記?。?/p>
- “遇到任何棘手的事情不要抱怨?!?/li>
- “一個優(yōu)秀的軟件工程招聘進來就是解決問題的,而不是制造問題;”
- “對于任務的安排,高手永遠都是說出解決問題的期限;到點交東西。而不是支支吾吾說不清楚、退縮。”
- “遇到問題冷靜思考,相信自己一定可以的;那怕失敗去嘗試一下也好?!?/li>
- “沒解決問題的時候不要說任何話,說什么都像是在找理由。閉上嘴巴去想辦法?!?/li>
其實解決這個問題時期發(fā)生了很多有趣的故事,不過最終還是要解決難啃的問題證明自己,開發(fā)學習本身就是一個不斷變強的過程“修技術,也修內心”當自己逐漸變強之后也不要鄙視技術不好的同事始終保持一顆學徒的心。
Part4,彩蛋
解決這個問題之后在同部門同事的眼里威望都會有提升(尤其是測試部門的小姐姐,因為她們不用費力的每天去看服務器了),最終解決項目的重大事故部門老大給了機會調到其他省的研發(fā)中心當項目經理薪資平移的基礎上再上浮百分之十。可見掌握一手救急的技能有多么劃算。
以上就是.Net程序內存異常的原因及解決的詳細內容,更多關于.Net程序內存異常的資料請關注腳本之家其它相關文章!
相關文章
python scrapy項目下spiders內多個爬蟲同時運行的實現
這篇文章主要介紹了python scrapy項目下spiders內多個爬蟲同時運行的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
Asp.net core利用IIS在windows上進行托管步驟詳解
這篇文章主要給大家介紹了關于Asp.net core利用IIS在windows上進行托管步驟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-03-03

