Quantcast
Viewing all articles
Browse latest Browse all 428

突破 32 位元 .NET 程式 2GB 記憶體上限

同事分享了一記讓 32 位元 .NET 程式突破 2GB 記憶體上限的密技,讓我不禁獻上了膝蓋,當然要轉分享一下。

.NET 編譯成 32 位元與 64 位元最大的差異在於可用記憶體上限,32 位元的記憶體定址上限為 4GB,其中 2GB 配置給作業系統核心模式,應用程式為使用者模式只有 2GB 可用,實際執行需再扣除 Runtime 本身耗用的記憶體,依經驗只能用到 1.6GB 左右。所以若無特殊限制,程式最好編譯成 AnyCPU 或 x64 以充分享用記憶體。但實務上 .NET 程式一旦引用了 32 位元 Unmanaged 元件,就毫無選擇只能以 32 位元執行。

Windows 有個 /3GB 啟動參數,可調整只配置 1GB 給核心模式,留下 3GB 給應用程式使用,但 /3GB 的設定步驟繁瑣,要部署大量客戶端很有麻煩。Visual C++ 有個 EDITBIN 命令列工具,可修改 OBJ/DLL/EXE 檔案旗標,其中有個 /LARGEADDRESSAWARE 參數可針對特定 EXE 開放 3GB 模式,突破 1.6GB 上限。

以下是個簡單的測試程式,透過不斷產生 1M 長度字串消耗記憶體直接 OutOfMemoryException,並用以前介紹過的記憶體用量觀察函式測量佔用的 Managed Heap 記憶體:

using System;
using System.Collections.Generic;

namespace _32BitAppTest
{
    public class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CreateBigData();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString()); 
            }
            Console.WriteLine("Press any key for exit... ");
            Console.ReadKey();
        }

        static void DumpMemSize(int count)
        {
            //強制回收記憶體清出空間,以充分利用所有記憶體 
            var memSz = GC.GetTotalMemory(true) / 1024 / 1024;
            Console.WriteLine(
                $"Managed Heap={memSz}MB, Count={count}");
        }

        static void CreateBigData()
        {
            var dic = new Dictionary<long, string>();
            var src = new string(' ', 1024 * 1024 - 1);
            for (int i = 0; i < 4096; i++)
            {
                dic.Add(i, src + (i % 10));
                if (i % 32 == 0)
                     DumpMemSize(i);                
            }
        }
    }
}

實測結果,大約用到 1.5GB 左右出現 OutOfMemoryException,跟一般認知的 2GB 上限相近。

Image may be NSFW.
Clik here to view.

用 EDITBIN /LARGEADDRESSAWARE 32BitAppTest.exe 開光後,同一支 32 位元 .NET程式便能吃到 3GB 記憶體,神奇吧!

Image may be NSFW.
Clik here to view.

如果要每次編譯後自動修改,可加在專案 Post-Build Event,以下是適用 VS2015/VS2017 的寫法:

Image may be NSFW.
Clik here to view.

使用以下腳本則可適用多個 VS 版本:參考

IF  EXIST  "%VS140COMNTOOLS%"  CALL  "%VS140COMNTOOLS%vsvars32.bat"
IF  EXIST  "%VS120COMNTOOLS%"  CALL  "%VS120COMNTOOLS%vsvars32.bat"
IF  EXIST  "%VS110COMNTOOLS%"  CALL  "%VS110COMNTOOLS%vsvars32.bat"
IF  EXIST  "%VS100COMNTOOLS%"  CALL  "%VS100COMNTOOLS%vsvars32.bat"
editbin.exe /LARGEADDRESSAWARE $(TargetPath)

補充,EDITBIN 為 Visual C++ 附屬工具,Visual Studio 記得要安裝 Visual C++ 才有的用。

【同場加映】

C/C++ Build Tools 有另一件工具 - DUMPBIN,可檢查 EXE 是否已設定 LARGEADDRESSAWARE 旗標,如下圖:

Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 428

Trending Articles