对 .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
做了这两步之后,继续让程序跑起来,截图如下:

从图中可以清晰的看到,终于有出息了。
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 。
三:总结
本篇说了这么多,其实都是一些不得已而为之的事情,很心酸,这世上很多东西不是靠技术就能解决的,更需要靠人情事故!
对 .NET程序2G虚拟地址紧张崩溃 的最后一次反思的更多相关文章
- Android DevArt4:IntentFilter学习及深入~问题描述:在不指定具体action前提下,如果有两个以上的Activity,具有完全相同的intent-filter,项目同步是否会出现异常?程序运行是否会崩溃?
概述:GitHub IntentFilter意图过滤器,三种匹配规则:action.category.data 重点:过滤规则中必须设置 '<category android:name=&quo ...
- Qt程序继承QApplication发生崩溃的原因
一.前情介绍 QApplication是Qt开发中经常用到的一个类,用来管理应用程序的生命周期.跟其相关的类还有QCoreApplication和QGuiApplication,分别用于不同场景下为应 ...
- golang程序因未知错误崩溃时如何记录异常
开发服务器程序时如果未经过充分测试, 服务稳定运行一段时间后会突然崩溃退出.一般是因为程序中出现了某个未捕获的异常. 这类问题属于偶现的,且需要服务器运行一段时间之后才会出现,难以定位有问题的代码段. ...
- Qt5.11.2 VS2015编译activemq发送程序 _ITERATOR_DEBUG_LEVEL错误和崩溃解决
1.问题描述: 运行环境是 win10 64位系统,开发环境是VS2015 ,Qt 5.11.2.开发activemq发送程序,遇到问题 (1)Qt5AxContainer.lib error LNK ...
- 论try/catch的重要性,我们经常遇到代码出现无法调试的错误,程序退出的时候崩溃。这跟我们代码日常保护的习惯息息相关。
每当构造函数或析构函数中出现溢出,会导致调试非常困难,而使用try/catch来处理构造中的初始化就非常重要了. 如上图,在构造函数中,我们的很多初始化动作会放在这里,但是却忽视了,一旦初始化出错了, ...
- 记一次 .NET 差旅管理后台 CPU 爆高分析
一:背景 1. 讲故事 前段时间有位朋友在微信上找到我,说他的 web 系统 cpu 运行一段时候后就爆高了,让我帮忙看一下是怎么回事,那就看吧,声明一下,我看 dump 是免费的,主要是锤炼自己技术 ...
- 你的java/c/c++程序崩溃了?揭秘段错误(Segmentation fault)(3)
前言 接上两篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault (1) 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2) 写到这里,越跟 ...
- 关于编写Java程序让Jvm崩溃
今天在书上看到一个作者提出一个问题“怎样通过编写Java代码让Jvm崩溃”,我看了之后也不懂.带着问题查了一下,百度知道里面有这样一个答案: package jvm; public class Cra ...
- 水火难容:同步方法调用async方法引发的ASP.NET应用程序崩溃
之前只知道在同步方法中调用异步(async)方法时,如果用.Result等待调用结果,会造成线程死锁(deadlock).自己也吃过这个苦头,详见等到花儿也谢了的await. 昨天一个偶然的情况,造成 ...
- [CareerCup] 12.2 Find the Reason of Crash 找到程序崩溃的原因
12.2 You are given the source to an application which crashes when it is run. After running it ten t ...
随机推荐
- P7167 Fountain
原题 有n个盘子组成的塔,向第i个盘子上倒水,若溢出会落到下面第一个直径大的盘子里,直到落到底部的水池为止.现给出q次询问,\((r,v)\)代表向第r个盘子里倒入体积为v的水,求水最终会停在哪个盘子 ...
- NativeBuferring,一种零分配的数据类型[下篇]
上文说到Unmanaged.BufferedBinary和BufferedString是NativeBuffering支持的三个基本数据类型,其实我们也可以说NativeBuffering只支持Unm ...
- MQ消息队列篇:三大MQ产品的必备面试种子题
MQ有什么用? MQ(消息队列)是一种FIFO(先进先出)的数据结构,主要用于实现异步通信.削峰平谷和解耦等功能.它通过将生产者生成的消息发送到队列中,然后由消费者进行消费.这样,生产者和消费者之间就 ...
- Redis从入门到放弃(7):主从复制
1.概念 主从复制是Redis的一项重要特性,用于将一个Redis服务器(Master主节点)的数据复制到其他Redis服务器(Slave从节点),以实现数据的高可用性和读写分离.数据的复制是单向的, ...
- Hugging Face 的文本生成和大语言模型的开源生态
[更新于 2023 年 7 月 23 日: 添加 Llama 2.] 文本生成和对话技术已经出现多年了.早期的挑战在于通过设置参数和分辨偏差,同时控制好文本忠实性和多样性.更忠实的输出一般更缺少创造性 ...
- Go协程揭秘:轻量、并发与性能的完美结合
Go协程为并发编程提供了强大的工具,结合轻量级.高效的特点,为开发者带来了独特的编程体验.本文深入探讨了Go协程的基本原理.同步机制.高级用法及其性能与最佳实践,旨在为读者提供全面.深入的理解和应用指 ...
- 大模型时代,如何快速开发AI应用
本文分享自华为云社区 <[云享问答]第3期:大模型时代,如何快速开发AI应用>,作者:华为云社区精选. 大模型快速普及应用的当下,AI浪潮汹涌而至,对于开发者来说,开发一款属于自己的AI应 ...
- Django框架项目之支付功能——支付宝支付
文章目录 支付宝支付 入门 支付流程 aliapy二次封装包 GitHub开源框架 依赖 结构 alipay_public_key.pem app_private_key.pem setting.py ...
- Redis系列之——Redis-Sentinel
文章目录 一 主从复制高可用 二 架构说明 三 安装配置 四 客户端连接 一 主从复制高可用 #主从复制存在的问题: #1 主从复制,主节点发生故障,需要做故障转移,可以手动转移:让其中一个slave ...
- linux日常维护(二)
linux启动流程 BIOS自检 启动GRUB 2 加载内核 执行systemd进程 初始化系统环境 执行/bin/login程序 (一)BIOS自检 加电POST自检(对硬件进行检测) 进行本地设备 ...