一生行走 发表于 2023-11-24 18:48:15

对 .NET程序2G虚拟地址紧张崩溃 的最后一次反思

一:背景

1. 讲故事

最近接连遇到了几起 2G 虚拟地址紧张 导致的程序崩溃,基本上 90% 都集中在医疗行业,真的很无语,他们用的都是一些上古的 XP,Windows7 x86,我也知道技术人很难也基本无法推动硬件系统和设备的升级,这里蕴含了巨大的人情世故。
写这一篇的目的是想系统化的整理一下如何配置 3G 开关让程序吃到更多的内存,让程序崩溃的不那么频繁一些,以及如何验证是否成功开启!
二:32位操作系统

1. 测试代码

首先大家要有一个理念:就是 32bit系统上跑的程序,默认只能吃到 2G 内存,因为这涉及到公平,用户态吃2G,内核态吃2G,为了方便演示,向一个 List 塞入 5000w 的 string,大概占用 2G 内存,然后把程序跑在 Windows7 32bit 操作系统上。
      static void Main(string[] args)
      {
            var list = new List<string>();

            for (int i = 0; i < 50000000; i++)
            {
                list.Add(i.ToString());

                if (i % 10000 == 0) { Console.WriteLine($"i={i}"); }
            }
            Console.WriteLine("ok");
            Console.ReadLine();
      }
从图中可以清楚的看到当内存到了631M 的时候就扛不住了,可能有些朋友好奇,为什么才这么点就不行了,这是因为 List 的底层是 2倍 扩容,所以内存大概会涨到 0.63G + 1.2G = 1.83G。
有些朋友可能会问,这不是还没到2G吗?一般来说内存到了 1.2G+ 的时候崩溃风险就会剧增,这个要谨记!
2. 如何解决

刚才也说了,医疗行业现状如此,只能通过人情世故去推动,那这 2G 数据真的无处安放吗? 这时候就只能启动 3G 开关,那如何启动呢?

[*]开启程序级的 Large Address Aware
这个 Large Address Aware 字段俗称大地址,途径就是在 PE 头里打开一个开关,让Windows加载器决定是否给程序打开 3G 的绿色通道。
当然看 PE头 的工具有很多,对于.NET程序个人感觉最好的就是用 DnSpy,它把 File Header 中的 Characteristics 字段具化了,我们选中 Large Address Aware 复选框然后保存,截图如下:


[*]开启机器级别 3G 开关
在32bit操作系统上让用户态程序吃到 3G 内存这对操作系统来说是非常谨慎的,毕竟这对内核态是非常不公平的,言外之意就是让出自己的 1G 给用户态,这骚操作可能就会把自己坑惨,谨慎起见需要人工开启机器级别的 3G 开关,命令如下:
bcdedit /set IncreaseUserVa 3072做了这两步之后,继续让程序跑起来,截图如下:

从图中可以清晰的看到,终于有出息了。
更多操作系统配置,可参考这篇文章:https://www.autodesk.com.cn/support/technical/article/caas/sfdcarticles/sfdcarticles/CHS/How-to-enable-a-3GB-switch-on-Windows-Vista-Windows-7-or-Windows-XP-s.html?v=2018
3. 如何验证是否开启了 3G

这确实是一个好问题,最简单的方式就是用!address 观察下地址空间。
0:000> !address

BaseAddr EndAddr+1 RgnSize   Type       State               Protect             Usage
-----------------------------------------------------------------------------------------------
...
+ bffde000 bffdf000   1000 MEM_PRIVATE MEM_COMMITPAGE_READWRITE                     TEB      [~0; aa4.fb8]
+ bffdf000 bffe0000   1000 MEM_PRIVATE MEM_COMMITPAGE_READWRITE                     PEB      
+ bffe0000 bfff0000    10000 MEM_PRIVATE MEM_RESERVE PAGE_NOACCESS                      <unknown>

0:000> ? bfff0000/0x100000
Evaluate expression: 3071 = 00000bff上面卦中的 bfff0000 转换过来就是 3G,如果你看到的是这个值,那就恭喜你啦!
如果有朋友想问如何验证 dump程序是否开启了大地址,这个可以用windbg提供的 !dh 命令。
0:000> lm
start    end      module name
001e0000 001e8000   ConsoleApp4 C (pdb symbols)          D:\code\MyApplication\ConsoleApp4\obj\x86\Debug\ConsoleApp4.pdb
66dd0000 678c8000   mscorlib_ni   (deferred)            
678d0000 67e61000   mscorwks   (deferred)            
6c7a0000 6c83b000   msvcr80    (deferred)
...
0:000> !dh ConsoleApp4

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
   14C machine (i386)
       3 number of sections
EDB20AC7 time date stamp
       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
   122 characteristics
            Executable
            App can handle >2gb addresses
            32 bit word machine如果看到上面卦中的 App can handle >2gb addresses 字样就表示你开启成功啦!
三:64位操作系统

1. 如何吃更多内存

在 x64系统上就方便多了, 只需要做第一步开启 Large Address Aware 即可,毕竟 x64系统 的虚拟地址空间不要太充足,在 48根地址总线上就是2的48次方,所以开启大地址后,会给 x32 程序4G的寻址空间,即 2 的 32 次方。
接下来直接把刚才的 ConsoleApp4.exe 程序从 Windows7 x86 搬迁到 Windows 10 x64 系统上,然后用 windbg 附加运行, 跑完后使用 !address 查看。
0:007> !address

BaseAddr EndAddr+1 RgnSize   Type       State               Protect             Usage
-----------------------------------------------------------------------------------------------
+      0   c60000   c60000             MEM_FREE    PAGE_NOACCESS                      Free   
...
+ ff671000 ff680000   f000             MEM_FREE    PAGE_NOACCESS                      Free      
+ ff680000 ff6b3000    33000 MEM_MAPPEDMEM_COMMITPAGE_READONLY                      Other      
+ ff6b3000 ffff0000   93d000             MEM_FREE    PAGE_NOACCESS                      Free      

0:007> ? ffff0000 /0x100000
Evaluate expression: 4095 = 00000fff如果在你的卦中也看到了上面的 ffff0000 ,那就恭喜你,你程序的内存寻址空间扩展到了 4G 。
三:总结

本篇说了这么多,其实都是一些不得已而为之的事情,很心酸,这世上很多东西不是靠技术就能解决的,更需要靠人情事故!

来源:https://www.cnblogs.com/huangxincheng/archive/2023/11/24/17853851.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 对 .NET程序2G虚拟地址紧张崩溃 的最后一次反思