1. 程式人生 > >Visual Studio編譯出的程式,以32位模式執行的問題與解決

Visual Studio編譯出的程式,以32位模式執行的問題與解決

前段時間,線上有一個.net寫的服務程式,會不定期的卡死且無法恢復,需要手工重啟程序。
經過一段時間的跟蹤和效能監控,發現在卡死之前,記憶體都會上升到1.8G左右,然後就開始波動,重啟程序後,記憶體跌到較低的水平,再經過慢慢上升,幾天後升到1.8G又卡死……
簡單排查了一下記憶體上升的原因,是業務上有一些訊息要求長駐記憶體,所以會上升,並不是洩露。
於是做個簡單的技術改造,把長駐訊息轉存到Redis,問題解決。

問題真的解決了嗎?為啥記憶體到1.8G就會卡住程序呢?
通過查詢資料,發現32位的程式,最大可用記憶體是2G,通常到達1.8G左右就會報記憶體不足,進而引發頻繁的GC回收。
注:32位程式雖然最大定址空間是4G,但是系統會保留2G用於核心對映,所以32位程式最多用到2G記憶體。

難道是這個原因?登入到伺服器上看工作管理員,果然是以32位模式執行的:
這裡寫圖片描述
上圖中,可以看到,Carving.Job 和 DingTalk,都是執行在32位模式下,而Chrome是執行在64位模式下。
從這個截圖來看,我的服務程式確實是因為32位執行模式導致記憶體到1.8G就會卡住造成的了。
解決方法就是去Visual Studio裡,把專案設定為x64編譯,再發布到伺服器上,這回沒有32位字樣了,問題解決。

等會,Visual Studio預設是Any Cpu啊,這個不是自適應的嗎?
按官方解釋,AnyCpu將程式集編譯為在任意平臺上執行,
也就是在32位Windows上它是按32位執行,在64位Windows上它是按64位執行。
可是為什麼這個專案用AnyCpu編譯,在64位的Windows上卻是按32位執行的?
查找了一下,我有一箇舊的專案按AnyCpu編譯後,執行時也是64位,沒有問題,
對比了一下它跟有問題的專案,發現問題專案的csproj裡,多了一行:<Prefer32Bit>true</Prefer32Bit>
搜尋一下Prefer32Bit,看到這樣一句話:

【.NET 4.5】新增的 Prefer 32-bit target platform
這本來是一個很小的feature並且也沒有什麼模糊的地方,
關鍵是VS把這個設定成了預設值,當預設為Any CPU的時候,application會被編譯成32-bit mode.

測試了一下,新建.NET4.5及以上版本的控制檯專案、WinForm專案,都預設勾選首選32位:
這裡寫圖片描述
而建立Web專案或類庫專案時,或者建立.NET4.0及以下版本的專案時,這個選項是灰色不可選的,不存在問題。

哎,好吧,以後新建.NET4.5及以上版本的控制檯專案、WinForm專案時,都要記得取消勾選“首選32位”。
上面的那個專案,把x64改回AnyCpu,並取消“首選32位”,再編譯釋出,果然也正常了。

這回才是真正解決問題了

文章的最後,列舉2個我知道的32位與64位程式的差異點吧:
1、上面說的記憶體問題,32位程式最多用到2G記憶體(不考慮3G模式);
2、32位和64位程式,GetHashCode的結果是不一樣的
其它什麼效能差異之類的,我沒有實際去對比過,大家各自搜尋一下或自動驗證一下吧