翼度科技»论坛 编程开发 .net 查看内容

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

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
一:背景

1. 讲故事

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

1. 测试代码

首先大家要有一个理念:就是 32bit系统上跑的程序,默认只能吃到 2G 内存,因为这涉及到公平,用户态吃2G,内核态吃2G,为了方便演示,向一个 List 塞入 5000w 的 string,大概占用 2G 内存,然后把程序跑在 Windows7 32bit 操作系统上。
  1.         static void Main(string[] args)
  2.         {
  3.             var list = new List<string>();
  4.             for (int i = 0; i < 50000000; i++)
  5.             {
  6.                 list.Add(i.ToString());
  7.                 if (i % 10000 == 0) { Console.WriteLine($"i={i}"); }
  8.             }
  9.             Console.WriteLine("ok");
  10.             Console.ReadLine();
  11.         }
复制代码

从图中可以清楚的看到当内存到了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 开关,命令如下:
  1. 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 观察下地址空间。
  1. 0:000> !address
  2.   BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
  3. -----------------------------------------------------------------------------------------------
  4. ...
  5. + bffde000 bffdf000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     TEB        [~0; aa4.fb8]
  6. + bffdf000 bffe0000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     PEB        [aa4]
  7. + bffe0000 bfff0000    10000 MEM_PRIVATE MEM_RESERVE PAGE_NOACCESS                      <unknown>  
  8. 0:000> ? bfff0000/0x100000
  9. Evaluate expression: 3071 = 00000bff
复制代码
上面卦中的 bfff0000 转换过来就是 3G,如果你看到的是这个值,那就恭喜你啦!
如果有朋友想问如何验证 dump程序是否开启了大地址,这个可以用windbg提供的 !dh 命令。
  1. 0:000> lm
  2. start    end        module name
  3. 001e0000 001e8000   ConsoleApp4 C (pdb symbols)          D:\code\MyApplication\ConsoleApp4\obj\x86\Debug\ConsoleApp4.pdb
  4. 66dd0000 678c8000   mscorlib_ni   (deferred)            
  5. 678d0000 67e61000   mscorwks   (deferred)            
  6. 6c7a0000 6c83b000   msvcr80    (deferred)  
  7. ...
  8. 0:000> !dh ConsoleApp4
  9. File Type: EXECUTABLE IMAGE
  10. FILE HEADER VALUES
  11.      14C machine (i386)
  12.        3 number of sections
  13. EDB20AC7 time date stamp
  14.        0 file pointer to symbol table
  15.        0 number of symbols
  16.       E0 size of optional header
  17.      122 characteristics
  18.             Executable
  19.             App can handle >2gb addresses
  20.             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 查看。
  1. 0:007> !address
  2.   BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
  3. -----------------------------------------------------------------------------------------------
  4. +        0   c60000   c60000             MEM_FREE    PAGE_NOACCESS                      Free     
  5. ...
  6. + ff671000 ff680000     f000             MEM_FREE    PAGE_NOACCESS                      Free      
  7. + ff680000 ff6b3000    33000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      Other      [NLS Tables]
  8. + ff6b3000 ffff0000   93d000             MEM_FREE    PAGE_NOACCESS                      Free      
  9. 0:007> ? ffff0000 /0x100000
  10. Evaluate expression: 4095 = 00000fff
复制代码
如果在你的卦中也看到了上面的 ffff0000 ,那就恭喜你,你程序的内存寻址空间扩展到了 4G 。
三:总结

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

来源:https://www.cnblogs.com/huangxincheng/archive/2023/11/24/17853851.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具