启动时分配和释放大量内存是否“清理内存”?


18

游戏编码完成》第四版,第5章(游戏初始化和关闭),“ 检查内存”部分包含以下有趣的代码示例:

bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded)
{
    MEMORYSTATUSEX status; 
    GlobalMemoryStatusEx(&status);
    if (status.ullTotalPhys < physicalRAMNeeded) 
    {
        // you don’t have enough physical memory. Tell the player to go get a 
        // real computer and give this one to his mother. 
        GCC_ERROR("CheckMemory Failure: Not enough physical memory."); 
        return false;
    }
    // Check for enough free memory.
    if (status.ullAvailVirtual < virtualRAMNeeded) 
    {
        // you don’t have enough virtual memory available.
        // Tell the player to shut down the copy of Visual Studio running in the 
        // background, or whatever seems to be sucking the memory dry. 
        GCC_ERROR("CheckMemory Failure: Not enough virtual memory.");
        return false;
    }

    char *buff = GCC_NEW char[virtualRAMNeeded]; 
    if (buff)
    {
        delete[] buff;
    }
    else
    {
        // even though there is enough memory, it isn't available in one
        // block, which can be critical for games that manage their own memory 
        GCC_ERROR("CheckMemory Failure: Not enough contiguous memory."); 
        return false;
    }
}

这引起了一些问题。

第一部分仅询问OS(Windows)有多少物理RAM可用。奇怪的是第二部分,它分配了大量的内存并立即释放它:

char *buff = GCC_NEW char[virtualRAMNeeded]; 
if (buff)
{
    delete[] buff;
}

作者继续解释:

...该函数分配并立即释放巨大的内存块。这样可以使Windows清理内存管理器中累积的所有垃圾,并仔细检查是否可以分配所需的连续块。如果调用成功,那么实际上您已经在系统内存中运行了相当于Zamboni机器的设备,从而为您的游戏做好了准备……

但我对此保留意见。

“清除内存管理器中累积的垃圾吗?” 真?如果游戏才刚刚开始,就应该没有垃圾吗?

“确定可以分配一个连续的块吗?” 在非常特殊的情况下,您将自己管理内存,这会使它们有意义,但是,即使您确实分配了很多内存,蝙蝠也几乎使其他任何应用程序都无法在其中运行开启系统时。

此外,这是否可能迫使操作系统提交所有这些内存,并因此将大量内存移出交换磁盘空间,从而大大降低了应用启动速度?

这真的是一个好习惯吗?


3
当应用程序分配大容量内存时,大多数现代OS都不会执行任何操作,它们会使用乐观分配,并且在您填满内存之前实际上不会做任何事情,我无法想象除了做一个潜在的缓慢无操作之外,其他任何事情都无法做
有价值

在丛林中清理长条地面,用木头雕刻收音机,耳机等会导致飞机降落并运送物资吗?
Dan Neely 2014年

10
如果您允许我,Game Coding Complete包含很多废话,以及许多不太了解的C ++和看起来有点像C ++的C(也在此示例中进行了演示,请检查operator newfor 的结果nullptr)说。那本书可以做的最好的事情就是点燃烟囱。分配和释放大块内存当然不会 “清理”内存。
戴蒙2014年

@Damon,我没有检查,但我怀疑它们至少已使全局new运算符重载为返回null而不是throw bad_alloc。如果没有,那么是的,此代码甚至更荒谬:P
glampert 2014年

1
@glampert:即使是这种情况,operator delete也必须接受nullptr并将其视为无操作。任何不这样做的全局重载都将被破坏。这意味着这都是荒谬的。就像假设分配一个巨大的内存块并释放它会“神奇地”做某件事一样。充其量,它不会造成任何伤害(很可能是因为页面甚至没有被触摸到……否则它可能会从工作集中交换掉一些页面,您稍后需要重新加载)。
戴蒙2014年

Answers:


12

有一两件事,预分配一大块内存可以做,如果你的电脑是低的在RAM,可能会迫使OS通过交换的其他程序的内存空间部分的磁盘以释放一些额外的空间。

由于这种交换通常是非常缓慢的操作,因此在程序运行时几乎会冻结程序,因此,如果有可能确保程序发生,它将您的游戏开始之前而不是在游戏进行中发生,这可能会带来一些好处。

就是说,强制这样的磁盘交换并非完全100%可靠:

  • 在许多虚拟内存实现中,仅分配内存实际上并不会触发任何交换。而是只有在您第一次访问分配的内存的每一页时才会发生。(在启用了内存过量使用的现代Linux版本上,这当然是正确的,因为默认情况下是这样;我不确定Windows的不同版本如何处理它。)

    因此,如果您确实希望此代码“清理内存”,则可能应添加一个memset()调用或等效项,以physicalRAMNeeded在释放数据之前将数据实际写入数组的每个部分(或至少写入数组的第一个字节)。

  • 另外,迫使操作系统交换其他程序了这样的RAM的并不意味着他们会留下来的吧。Windows是一个多任务操作系统,一旦其中一个换出的程序想要再次运行并使用其换出的数据,它将被换回,从而可能再次冻结您的游戏。

    因此,该技巧通常仅在RAM被大量未被主动访问的数据(例如,某些编辑软件在后台打开的大文件)吞噬了时才有用。在这方面,Web浏览器尤其成问题,因为即使您实际上并未使用在某些“背景”选项卡中打开的网页,该页面上仍可能运行有脚本,将其强制保留在RAM中。

    (有一种方法可以使程序实际上告诉操作系统将其部分存储空间锁定到RAM中并防止其被换出,但是这些方法通常需要提升的特权。在任何情况下,您发布的代码似乎都不是使用任何此类方法。)

基本上,此技巧似乎仅在相对狭窄的边界情况下有用,在这种情况下,用户有足够的RAM来运行您的游戏(以及任何其他在后台主动运行的软件)而无需交换,但是该RAM当前充满了不活动和未使用的打开文件或其他可以并且应该在游戏运行时交换到磁盘的数据。在这种情况下,通过在启动过程中将偶尔的小交换操作替换为启动过程中的单个小交换操作,可能会有效地使您的游戏看起来更流畅,响应速度更快。


1
根据我对低级内存管理的有限了解,我可以说是否交换特定页面的决定要复杂得多,并且不仅考虑所请求的内存量和可用内存量,还需要考虑更多因素。 。过度简化这一过程并通常依赖于假设并不是很明智。
熊猫睡衣2014年

3
我认为重要的是要记住,所有这些假设都可能起作用,而不起作用,或者似乎起作用,但是出于完全不相关的原因。据我所知,答案中没有指出任何行为,因此依靠它们是不明智的。操作系统更新,设置更改,编译器更改或几乎任何事情都可能导致此操作停止,甚至对性能产生负面影响。
熊猫睡衣2014年

26

我不知道这篇文章有多老,但我会说它已经很老了。在现代Windows(XP和更高版本,但特别是在64位版本上)中,我想说这样做对游戏启动几乎没有负面影响。

首先,请记住,每个进程的地址空间都是虚拟的。就您的进程而言,您拥有自己的整个地址空间,并且始终会获得连续的(虚拟)内存,而不管该内存在物理上是连续的。

实际上,内存是否在物理内存中是连续的不是您可以控制的。操作系统将选择合适的方式分配物理块,并且您在应用程序级别执行的任何操作都不会影响拥有连续物理内存的好处(如果有)。

如果您要在很长一段时间内继续分配和释放不同大小的内存,则确实需要关心虚拟内存碎片。当然,这与64位地址空间无关。游戏通常不会运行数月,因此,除非我们要谈论长期运行的游戏服务器,否则您很可能不必在意这一点。

即使您确实在32位计算机中分配了大量大小完全不同的内存,现代内存分配也相当不错,因此除非您积极寻找虚拟内存碎片问题,否则您不太可能会遇到虚拟内存碎片问题

老实说,我不知道那个作家在做什么。即使地址空间是共享的,并且确实在物理内存中获得了一个巨大的连续块,但通过释放它,您的意思是您不再关心它。还有其他一些在后台运行的程序在做自己的分配,因此,即使您的大型malloc成功了,也没有人能保证下一个程序也会成功。

我认为这是“由于某种原因,我无法真正理解它,所以我做了一些解释。”


10
RAM与旋转的硬盘不同,它具有连续的块比以任何方式“更好”。 这不是真的。连续RAM访问比随机访问快一个数量级。RAM芯片分为若干段,需要切换一定的延迟时间,更重要的是,当内存分散时,L1和L2高速缓存未命中将严重影响您的性能。
CaptainCodeman 2014年

@CaptainCodeman:我必须接受这超出了我的知识领域,但是无论如何,作为Windows应用程序程序员,您无法控制内存在物理上是连续的。要求巨大的内存块不会有太大变化。
熊猫睡衣2014年

@CaptainCodeman实际上有一个连续的虚拟块位于RAM中的连续mod 2 ^ N块中,这是由于缓存行分配而使其加速
棘轮异常2014年

8
@CaptainCodeman是的,顺序DRAM访问速度更快,但是我们谈论的是虚拟->物理映射,该映射发生在4 KB(或更多)的页面中,因此,该映射是否为顺序映射对内存不会有太大影响阅读速度。另外,缓存预取和类似的操作在虚拟地址空间(而非物理地址)中进行。
内森·里德

1
@NathanReed公平,感谢您的澄清。正如我所建议的,我只是表示RAM访问速度在整个存储空间中并不完全一致。
CaptainCodeman 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.