在调试模式下,动态内存分配似乎是瞬间完成的,但在发布模式下则逐渐进行。

4

我有一个大的动态分配的数组(C ++、MSVC110),我像这样初始化它:

try {
    size_t arrayLength = 1 << 28;
    data = new int[arrayLength];
    for (size_t i = 0; i < arrayLength; ++i) {
        data[i] = rand();
    }
}
catch (std::bad_alloc&) { /* Report error. */ }

在尝试分配超出系统实际内存的空间(如10 GB)之前,一切都很好。我本来期望能捕获到一个bad_alloc异常,但是系统(Win7)开始疯狂地交换内存等等,你知道我在说什么。
然后我在任务管理器中检查了一下情况,发现一个有趣的事情,在调试模式下,分配是瞬间完成的,但在发布模式下,它是逐步进行的。
调试模式: Debug mode allocation graph 发布模式: Release mode allocation graph 这是什么原因?这会对性能产生任何负面影响吗?我做错了什么吗?是操作系统引起的还是C++分配器引起的?
实际上,我更希望在没有足够内存时得到异常,而不是进入无休止的交换循环。在C ++中有什么方法可以实现这一点吗?
我知道一个解决方案可能是关闭Windows中的交换,但那只会为我解决问题。

6
我认为这是由于DEBUG C标准库代码将某些调试信息放入任何分配的内存中,从而立即访问它并导致其被分页。在发布版本中,您可能会逐渐将内存分页,具体取决于您如何访问/使用它。 - MicroVirus
1
我同意MicroVirus的观点。请查看此文章,以获取有关Windows调试堆元数据的更多信息。 - Manu343726
@MicroVirus请发布你的答案。 - imreal
1个回答

2
我认为在调试模式下,内存分配器会进行一些链接操作,以便更好地检测内存处理错误。它将访问每个已分配的块,以在其中写入几个字节,从而迫使系统快速提交所有已分配的页面。
在发布模式下,是您的代码进行线性填充块,因此每次提交一个页面。
至于限制内存量,您可以使用系统调用来了解可用资源情况。例如,在Windows环境中,可以使用这些
如果内存交换被需要,使系统调用失败就没有意义,因为由于某些给定程序无法控制的情况(如启动其他应用程序),可用内存量会不断变化。
有可能使一些内存块不可交换(即锁定在RAM中),但这种用法通常仅限于驱动程序等系统层。
检测可用内存并强制执行分配限制取决于您。
请注意,这是一场危险的游戏;由于通常不是独自在计算机上运行,而且无法预测是否会稍后启动另一个应用程序并消耗更多内存。
如果交换对您的应用程序有影响,您应考虑留出安全裕度(例如,尝试留出500MB或1GB的RAM可用于系统)。

https://dev59.com/GnVC5IYBdhLWcg3w-mZO - drescherjm
@drescherjm 是的,但在此之前我怀疑分配器进行了其他访问,否则您应该观察到与发布版本相同的结果。 - kuroi neko
发布不会填充堆。 - drescherjm
@drescherjm 没问题。你的观点很有道理,澄清是受欢迎的。 - kuroi neko
唯一让我惊讶的是调试版本会如此快速地填充内存,而我的代码却要慢得多。也许是随机生成器 (std::mt199370) 减缓了运行速度。此外,我通过字符访问内存,这进一步减慢了迭代速度。 - NightElfik
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接