对 .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 复选框然后保存,截图如下:
在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_COMMIT PAGE_READWRITE TEB [~0; aa4.fb8]
- + bffdf000 bffe0000 1000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE PEB [aa4]
- + 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_MAPPED MEM_COMMIT PAGE_READONLY Other [NLS Tables]
- + 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】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|
|
|
发表于 2023-11-24 18:48:15
举报
回复
分享
|
|
|
|